<?php
//先必须加载缓存扩展类 at GhxCache.php

/**
 * USERSELF:内容发布者本人
 */
define('USERSELF',1000);
/**
 * 信息类型:thread
 */
define('TX_INFO_TYPE_THREAD',1);
/**
 * 信息类型:POST
 */
define('TX_INFO_TYPE_POST',2);
/**
 * 信息类型:FORUM 板块
 */
define('TX_INFO_TYPE_FORUM',3);
/**
 * 信息类型:USER
 */
define('TX_INFO_TYPE_USER',4);
/**
 * 信息类型:attach
 */
define('TX_INFO_TYPE_ATTACH',5);
/**
 * 网站代码的字符集编码:$charset
 */
$charset = ini_get('default_charset');

// ------------------------------------------------------------------------
/**
 * 删除指定前缀的缓存
 * @param string|null $CachePrefix2 缓存二级前缀,null为获取
 * @return int 返回成功删除缓存的个数
 */
function ClearCacheFor(string $CachePrefix2 = null)
{
    $dels = 0;
    $cachepre = '';
    $fullcp = '';
    $xncache = $_SERVER['cache'];
    if (!empty($xncache)) {
        $cachepre = $xncache->cachepre;
    }
    if ($CachePrefix2 === null) {
        $CachePrefix2 = GhxCache::getInstance()->CachePrefix;
    }
    $fullcp = $cachepre . $CachePrefix2;
    if (!empty($fullcp)) {
        $cond = array('k' => array(' LIKE ' => $fullcp . '%'));
        $caches = db_find('cache', $cond, array(), 1, 99999, 'k');
        if (!empty($caches)) {
            $cachekeys = array_keys($caches);
            $cachepre_len = strlen($fullcp);
            $allcaches = array('TB_Columns_postflag', 'PostFlagCfgForType', 'TB_Columns_forum', 'view_template_list');
            foreach ($cachekeys as &$cachekey) {
                $cachekey2 = substr($cachekey, $cachepre_len);
                if (!in_array($cachekey2, $allcaches))
                    $allcaches[] = $cachekey2;
            }
            foreach ($allcaches as &$cache) {
                if (cache_delete($CachePrefix2 . $cache))
                    $dels++;
            }
        }
    }
    return $dels;
}
//---------------------------------------------------------------------
if (!function_exists('table_column_add')) {
     /**
     * 向数据表中添加字段
     * @param string $table 表名,不要包含前缀
     * @param array $ddls 字段数据数组,可以是多组,包含的元素有:name,attr,comment?,也可能是0,1,2?
     * @return bool 是否成功
     */
    function table_column_add(string $table, array $ddls)
    {
        $exeret = add_table_columns($table,$ddls);
        if($exeret !== false){//刷新缓存
            global $ghx_cache;
            $ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $table,true);
        }
        return $exeret;
    }
}
if (!function_exists('table_column_drop')) {
    /**
     * 删除表的字段,兼容pdo_sqlite
     * @param string $table 表名,不要包含前缀
     * @param array|string $columns 字段列表
     * @return bool
     */
    function table_column_drop(string $table, $columns)
    {
        $exeret = drop_table_columns($table, $columns);
        if($exeret !== false){//刷新缓存
            global $ghx_cache;
            $ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $table,true);
        }
        return $exeret;
    }
}
// --------------------------------------------------------------------
if (!function_exists('get_filenames_ci3')) {
    /**
     * Get Filenames
     *
     * 获取指定目录下所有文件名组成的数组。如果需要完整路径的文件名， 可以将第二个参数设置为 TRUE 。
     * 获取指定目录下所有文件信息组成的数组，包括文件名、文件大小、日期 和 权限。 默认不包含子目录下的文件信息，如有需要，可以设置第二个参数为 FALSE ，这可能会是一个耗时的操作。
     * @param string    path to source
     * @param bool $include_path   whether to include the path as part of the filename
     * @param bool  $_recursion  internal variable to determine recursion status - do not use in calls
     * @param bool  $test_separator 是否检测目录分隔符
     * @return    array|false
     */
    function &get_filenames_ci3($source_dir, $include_path = FALSE, $_recursion = FALSE,bool $test_separator = true)
    {
        static $_filedata = array();
        if($test_separator){
            if(DIRECTORY_SEPARATOR == '/') $source_dir = str_replace('\\',DIRECTORY_SEPARATOR,$source_dir);
            else $source_dir = str_replace('/',DIRECTORY_SEPARATOR,$source_dir);
        }
        if ($fp = @opendir($source_dir)) {
            // reset the array and make sure $source_dir has a trailing slash on the initial call
            if ($_recursion === FALSE) {
                $_filedata = array();
                $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
            }
            else{
                if($test_separator && $source_dir[strlen($source_dir)-1] != DIRECTORY_SEPARATOR)
                    $source_dir .= DIRECTORY_SEPARATOR;
            }
            while (FALSE !== ($file = readdir($fp))) {
                if (is_dir($source_dir . $file) && $file[0] !== '.') {
                    get_filenames_ci3($source_dir . $file . DIRECTORY_SEPARATOR, $include_path, TRUE,false);
                } elseif ($file[0] !== '.') {
                    $_filedata[] = ($include_path === TRUE) ? $source_dir . $file : $file;
                }
            }
            closedir($fp);
            return $_filedata;
        }
        $false = false;
        return $false;
    }
}
//-------------------------------------------------------
/**
 * 初始化插件配置数组
 * @param array &$cfg 传入的配置数组
 * @param string $plugin 插件名称,若为空，则不执行
 * @param int $action 动作代码,默认为1
 * @return bool
 */
function InitializationGhxThreadExtCfg(array &$cfg, string $plugin='ghx_threadext', $action = 1)
{
    global $conf;
    $ret = false;
    if (!empty($cfg) && $action != 0) {
        if (!empty($plugin)) {
            $optNode = null;
            if (isset($cfg['ThreadExtensionAuthGroup']))
                $optNode = &$cfg['ThreadExtensionAuthGroup'];
            if (!empty($optNode)) {
                if (is_string($optNode)) {
                    $optNode = preg_split('#[^\d]+#s', $optNode, -1, PREG_SPLIT_NO_EMPTY);
                    foreach ($optNode as &$zhx) {
                        $zhx = intval($zhx);
                    }
                }
                if (!is_array($optNode)) {
                    $optNode = array(intval($optNode));
                }
            }
            unset($optNode, $zhx);

            $optNode = null;
            if (isset($cfg['AdjustPostSortRankAuthGroup']))
                $optNode = &$cfg['AdjustPostSortRankAuthGroup'];
            if (!empty($optNode)) {
                if (is_string($optNode)) {
                    $optNode = preg_split('#[^\d]+#s', $optNode, -1, PREG_SPLIT_NO_EMPTY);
                    foreach ($optNode as &$zhx) {
                        $zhx = intval($zhx);
                    }
                }
                if (!is_array($optNode)) {
                    $optNode = array(intval($optNode));
                }
            }
            unset($optNode, $zhx);

            $optNode = null;
            if (isset($cfg['ViewTemplate']['AdjustAuthGroup']))
                $optNode = &$cfg['ViewTemplate']['AdjustAuthGroup'];
            if (!empty($optNode)) {
                if (is_string($optNode)) {
                    $optNode = preg_split('#[^\d]+#s', $optNode, -1, PREG_SPLIT_NO_EMPTY);
                    foreach ($optNode as &$zhx) {
                        $zhx = intval($zhx);
                    }
                }
                if (!is_array($optNode)) {
                    $optNode = array(intval($optNode));
                }
            }
            unset($optNode, $zhx);

            $AKWCfg = null;
            if (isset($cfg['ArticleKeyWord']))
                $AKWCfg = &$cfg['ArticleKeyWord'];
            if (!empty($AKWCfg)) {
                if (isset($AKWCfg['ArrangementWeights'])) {
                    $awVal = &$AKWCfg['ArrangementWeights'];
                    if (is_string($awVal)) {
                        $awVal = preg_split('#[,;/|\s]+#s', $awVal, -1, PREG_SPLIT_NO_EMPTY);
                        foreach ($awVal as &$zhx) {
                            $zhx = intval($zhx);
                        }
                        unset($zhx);
                    }
                    if (!is_array($awVal)) {
                        $awVal = array(intval($awVal));
                    }
                    if (!is_double(current($awVal))) {
                        foreach ($awVal as &$awValp) {
                            if (strpos($awValp, '.') !== false)
                                $awValp = doubleval($awValp);
                            else
                                $awValp = intval($awValp);
                        }
                    }
                    unset($awVal);
                }
                if (isset($AKWCfg['SearchMatchSerialWeighted'])) {
                    $smwVal = &$AKWCfg['SearchMatchSerialWeighted'];
                    if (is_string($smwVal)) {
                        $smwVal = preg_split('#[,;\\\/|\s]+#s', $smwVal, -1, PREG_SPLIT_NO_EMPTY);
                        foreach ($smwVal as &$zhx) {
                            $zhx = doubleval($zhx);
                        }
                    }
                    if (!is_array($smwVal)) {
                        $smwVal = array(doubleval($smwVal));
                    }
                    if (!is_double(current($smwVal))) {
                        foreach ($smwVal as &$smwValp) {
                            $smwValp = doubleval($smwValp);
                        }
                    }
                    unset($smwVal);
                }
            }
            $set_paths = array();//配置值中有变量代码需要处理:
            if (isset($cfg['TitleImage'])) {
                $TitleImageCfg = &$cfg['TitleImage'];
                if (isset($TitleImageCfg['SavePath'])) {
                    if (is_string($TitleImageCfg['SavePath']) && strlen($TitleImageCfg['SavePath']) > 5)
                        $set_paths['SavePath'] = &$TitleImageCfg['SavePath'];
                    else {
                        $TitleImageCfg['SavePath'] = $conf['upload_url'] . 'titleimg/';
                    }
                }
                unset($TitleImageCfg);
            }
            if (isset($cfg['AttachImageWatermark'])) {
                $AttachImageWatermarkCfg = &$cfg['AttachImageWatermark'];
                if (isset($AttachImageWatermarkCfg['WatermarkSet'])) {
                    $WatermarkSet = &$AttachImageWatermarkCfg['WatermarkSet'];
                    if (isset($WatermarkSet['OriginalImgBakPath'])) {
                        if (is_string($WatermarkSet['OriginalImgBakPath']) && strlen($WatermarkSet['OriginalImgBakPath']) > 5)
                            $set_paths['OriginalImgBakPath'] = &$WatermarkSet['OriginalImgBakPath'];
                    }
                    if (isset($WatermarkSet['MarkImg'])) {
                        if (is_string($WatermarkSet['MarkImg']) && strlen($WatermarkSet['MarkImg']) > 5)
                            $set_paths['MarkImg'] = &$WatermarkSet['MarkImg'];
                    }
                    if (isset($WatermarkSet['WatermarkOutputPath'])) {
                        if (is_string($WatermarkSet['WatermarkOutputPath']) && strlen($WatermarkSet['WatermarkOutputPath']) > 5)
                            $set_paths['WatermarkOutputPath'] = &$WatermarkSet['WatermarkOutputPath'];
                    }
                    if (isset($WatermarkSet['ThumbnailOutputPath'])) {
                        if (is_string($WatermarkSet['ThumbnailOutputPath']) && strlen($WatermarkSet['ThumbnailOutputPath']) > 5)
                            $set_paths['ThumbnailOutputPath'] = &$WatermarkSet['ThumbnailOutputPath'];
                    }
                    unset($WatermarkSet);
                }
                unset($AttachImageWatermarkCfg);
            }
            if (!empty($set_paths)) {
                $lr = new LabelReplacer();
                $lr->vars = &$conf;
                $lr->default_val = null;
                $matchret = null;
                $repcounts = 0;
                foreach ($set_paths as $var_vn => &$set_path) {
                    if (($matchret = preg_replace_callback('/\{(\w+)}/su', array($lr, 'ReplaceStrVar'), $set_path, -1, $repcounts)) !== null) {
                        if ($repcounts > 0) {
                            if ($var_vn == 'SavePath' || $var_vn == 'OriginalImgBakPath') {
                                $_teimgpathlastchr = $matchret[strlen($matchret) - 1];
                                if ($_teimgpathlastchr != '/' && $_teimgpathlastchr != '\\')
                                    $matchret .= '/';//最后加上/
                            }
                            $set_path = $matchret;
                        }
                    }
                }
            }
            unset($set_paths);
            $postFlagCfg = null;
            if (isset($cfg['PostFlag'])) $postFlagCfg = &$cfg['PostFlag'];
            $columnOptErr = 0;
            if (!empty($postFlagCfg) && !empty($postFlagCfg['Flags'])) {
                //标记控制的默认值初始化:
                if (!empty($postFlagCfg['FlagControlDefault'])) {
                    $acts = array('AuthGroup', 'DisplayGroup');
                    $postFlagCfgFCD = &$postFlagCfg['FlagControlDefault'];
                    foreach ($acts as $act) {
                        if (isset($postFlagCfgFCD[$act]) && !empty($postFlagCfgFCD[$act])) {
                            $postFlagCfgFCD_fi = &$postFlagCfgFCD[$act];
                            foreach ($postFlagCfgFCD_fi as $_fiK => &$_fiV) {
                                if (!empty($_fiV) && is_string($_fiV)) {
                                    if (empty($_fiV) || $_fiV == '*' || $_fiV == 'all' || $_fiV == '全部')
                                        $_fiV = array();
                                    else {
                                        $_fiV = preg_split('#[^\d\-+]+#s', $_fiV, -1, PREG_SPLIT_NO_EMPTY);
                                        foreach ($_fiV as &$_fiAuthGrupV) {
                                            $_fiAuthGrupV = intval($_fiAuthGrupV);
                                        }
                                        unset($_fiAuthGrupV);
                                    }
                                }
                            }
                            unset($postFlagCfgFCD_fi);
                        }
                    }
                }

                $table = $postFlagCfg['table'];
                global $ghx_cache;
                //格式化Flags:
                if (empty($table)) $table = 'postflag';
                $tbColumns = &$ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $table);
                $curColumns = array('id', 'pid', 'hide', 'fid', 'tid', 'isfirst');//固有的字段,$curColumns当前标志
                $addColumns = array();//新增的标志
                $delColumns = array();//已经删除的标志
                foreach ($postFlagCfg['Flags'] as $flagName => &$flagVal) {
                    //为获取更快的查询速度,采用字段横向展开方式
                    array_push($curColumns, $flagName, $flagName . '_u', $flagName . '_g');
                    if (!array_key_exists($flagName, $tbColumns)) {
                        $addColumns[] = array(
                            'name' => $flagName . '_u',
                            'attr' => 'int(11) NULL',
                            'comment' => $flagVal['title'] . '标记者的用户ID'
                        );
                        $addColumns[] = array(
                            'name' => $flagName . '_g',
                            'attr' => 'smallint(6) NULL',
                            'comment' => $flagVal['title'] . '标记者的用户组ID'
                        );
                        $addColumns[] = array(
                            'name' => $flagName,
                            'attr' => 'varchar(512) NULL',
                            'comment' => $flagVal['title'] . '标记详情'
                        );
                    }
                    if (!isset($flagVal['enable'])) {
                        //加载enable默认值
                        if (isset($postFlagCfgFCD['GroupEnable'][$flagVal['type']]))
                            $flagVal['enable'] = (int)$postFlagCfgFCD['GroupEnable'][$flagVal['type']];
                        else
                            $flagVal['enable'] = 1;
                    }
                    if (empty($flagVal['order']))
                        $flagVal['order'] = 0;
                    else {
                        $flagVal['order'] = intval($flagVal['order']);
                        if ($flagVal['order'] > 100) $flagVal['order'] = 100;//最大值为100
                        elseif ($flagVal['order'] < -100) $flagVal['order'] = -100;//最小值为-100
                    }
                    if (isset($flagVal['AuthGroup'])) {
                        if (!is_array($flagVal['AuthGroup'])) {
                            $glines = strval($flagVal['AuthGroup']);
                            $glines = strtolower($glines);
                            if (empty($flagVal['AuthGroup']) || $glines == '*' || $glines == 'all' || $glines == '全部')
                                $flagVal['AuthGroup'] = array();
                            else {
                                $flagVal['AuthGroup'] = preg_split('#[^\d\-+]+#s', $glines, -1, PREG_SPLIT_NO_EMPTY);
                                $isappend = false;
                                if ($flagVal['AuthGroup'][0] == '+') {//在默认值的基础上增加:
                                    $isappend = true;
                                    array_shift($flagVal['AuthGroup']);
                                }
                                foreach ($flagVal['AuthGroup'] as &$AuthGrupV) {
                                    $AuthGrupV = intval($AuthGrupV);
                                }
                                //添加默认值,合并之:
                                if ($isappend && isset($postFlagCfgFCD['AuthGroup'][$flagVal['type']]) && !empty($postFlagCfgFCD['AuthGroup'][$flagVal['type']])) {
                                    $flagVal['AuthGroup'] = array_merge($flagVal['AuthGroup'], $postFlagCfgFCD['AuthGroup'][$flagVal['type']]);
                                }
                                //去除重复值:
                                $flagVal['AuthGroup'] = array_unique($flagVal['AuthGroup'], SORT_NUMERIC);
                            }
                        }
                    } else {
                        $flagVal['AuthGroup'] = array();
                        if (isset($postFlagCfgFCD['AuthGroup'][$flagVal['type']]) && !empty($postFlagCfgFCD['AuthGroup'][$flagVal['type']])) {
                            $flagVal['AuthGroup'] = &$postFlagCfgFCD['AuthGroup'][$flagVal['type']];
                        }
                    }
                    if (isset($flagVal['DisplayGroup'])) {
                        if (!is_array($flagVal['DisplayGroup'])) {
                            $glines = strval($flagVal['DisplayGroup']);
                            $glines = strtolower($glines);
                            if (empty($flagVal['DisplayGroup']) || $glines == '*' || $glines == 'all' || $glines == '全部')
                                $flagVal['DisplayGroup'] = array();
                            else {
                                $flagVal['DisplayGroup'] = preg_split('#[^\d\-+]+#s', $glines, -1, PREG_SPLIT_NO_EMPTY);
                                $isappend = false;
                                if ($flagVal['DisplayGroup'][0] == '+') {//在默认值的基础上增加:
                                    $isappend = true;
                                    array_shift($flagVal['DisplayGroup']);
                                }
                                foreach ($flagVal['DisplayGroup'] as &$DisplayGrupV) {
                                    $DisplayGrupV = intval($DisplayGrupV);
                                }
                                //添加默认值,合并之:
                                if ($isappend && isset($postFlagCfgFCD['DisplayGroup'][$flagVal['type']]) && !empty($postFlagCfgFCD['DisplayGroup'][$flagVal['type']])) {
                                    $flagVal['DisplayGroup'] = array_merge($flagVal['DisplayGroup'], $postFlagCfgFCD['DisplayGroup'][$flagVal['type']]);
                                }
                                //去除重复值:
                                $flagVal['DisplayGroup'] = array_unique($flagVal['DisplayGroup'], SORT_NUMERIC);
                            }
                        }
                    } else {
                        $flagVal['DisplayGroup'] = array();
                        if (isset($postFlagCfgFCD['DisplayGroup'][$flagVal['type']]) && !empty($postFlagCfgFCD['DisplayGroup'][$flagVal['type']])) {
                            $flagVal['DisplayGroup'] = &$postFlagCfgFCD['DisplayGroup'][$flagVal['type']];
                        }
                    }
                    //填充默认SubjectFmt:
                    if (!isset($flagVal['SubjectFmt']) && isset($postFlagCfgFCD['SubjectFmt'])) {
                        $flagVal['SubjectFmt'] = &$postFlagCfgFCD['SubjectFmt'];//使用引用,有利于即时修改
                    }
                }
                foreach ($tbColumns as $tbColumn => &$ftype) {
                    if (!in_array($tbColumn, $curColumns)) {
                        $delColumns[] = $tbColumn;
                    }
                }
                if (!empty($delColumns)) {
                    $optRet = drop_table_columns($table, $delColumns);
                    if ($optRet === false) $columnOptErr++;
                }
                if (!empty($addColumns)) {
                    $optRet = add_table_columns($table, $addColumns);
                    if ($optRet === false) $columnOptErr++;
                }
                unset($postFlagCfgFCD);
            }
            $LabelCfg = null;
            if (isset($cfg['Label'])) $LabelCfg = &$cfg['Label'];
            if (!empty($LabelCfg)) {
                if (!empty($LabelCfg['SubLabellog'])) {
                    $tmpSL = array();
                    foreach ($LabelCfg['SubLabellog'] as $subK => &$max) {
                        $tbn = null;
                        if (is_numeric($subK)) $tbn = intval($subK);
                        $matches = null;
                        if (preg_match('#(\d{3,20})#', $max, $matches) > 0) {
                            $max = intval($matches[1]);
                            if ($tbn === null) $tmpSL[] = $max;
                            else $tmpSL[$tbn] = $max;
                        }
                    }
                    $LabelCfg['SubLabellog'] = $tmpSL;
                } else
                    $LabelCfg['SubLabellog'] = array();//必须为数组类型
            }
            $ret = true;
        }
    }
    return $ret;
}
/**
 * 替换PostFlag详情字符串里的{var},会使用到$user
 * @param $text
 * @param array|null $postFlagCfg postFlag的配置节点
 * @param array $arr 额外的变量表
 * @return string|string[]
 */
function &FillPostFlagText($text, array &$postFlagCfg = null, $arr = array())
{
    global $user;
    if ($arr === null) $arr = array();
    if (!in_array('time', $arr))
        $arr['time'] = date_format(date_create(), 'Y/m/d H:i:s');
    if (!in_array('userid', $arr)) {
        $arr['userid'] = $user['uid'];
        $arr['groupid'] = $user['gid'];
        $arr['groupname'] = &$user['groupname'];//防止修改
        $arr['username'] = &$user['username'];//防止修改
        if (!in_array('title', $arr) && !empty($postFlagCfg) && !empty($postFlagCfg['title'])) {
            foreach ($postFlagCfg as $k => &$v) {
                if (!is_array($v) && !is_object($v)) {
                    if (is_string($k)) $arr[$k] = $v;
                    else  $arr[$k] = strval($v);
                }
            }
        }
    }
    $ret = &filltextvar($text, $arr,'');
    return $ret;
}


/**
 * 将web request data转换为三维数组
 * @param string $str web request data
 * @param string|array $web_request_data_delimiters 分隔符,第0个为键值分隔符,第1个为第一层次的分隔符,以此类推
 * @param array|null $dimensions 参数维度规则,有几个元素就代表有多少层次;注意:JSON格式不会以字符串的方式解析
 * 格式:array('key1'=>array('keytype'=>'int','defaultkey'=>{key},'key'=>true|false,'defaultval'=>{value},'onlyvalue'=>true|false,'namekey'=>{key1},'nameval'=>{key2}...),
 * keytype:类型,defaultkey:键不存在时的默认值,defaultval:值不存在时的默认值,如果defaultval===false,当从字符串析出的值部分为空时,忽略此项;
 * onlyvalue:是否只有值,公最后一维使用,比如flag的clear操作
 * namekey/nameval:最后一维的键值用array包装,并以namekey/nameval作为他们的键名
 * @param array|null $params 传入要合并的参数,如果$params中存在新解析参数中的键名,则会将它变为数组,并附加上新值
 * @return array|false
 */
function &parse_web_request_data_items(string $str, $web_request_data_delimiters, array &$dimensions = null, array &$params = null)
{
    $ret = false;
    if (!empty($str)) {
        if ($str[0] == '{' && $str[strlen($str) - 1] == '}') {
            $ret = xn_json_decode($str);
        } else {
            if (empty($web_request_data_delimiters)) $web_request_data_delimiters = ':|;,';
            if (is_string($web_request_data_delimiters)) {
                $wrddlen = strlen($web_request_data_delimiters);
                $web_request_data_delimiters_opt = array();
                for($ec=0;$ec<$wrddlen;$ec++){
                    $web_request_data_delimiters_opt[] = $web_request_data_delimiters[$ec];
                }
                $web_request_data_delimiters = &$web_request_data_delimiters_opt;
            }
            //post中可能有pid值
            if (empty($dimensions)) $dimensions = array('pid' => array('keytype' => 'int', 'defaultkey' => _POST('pid', 0)), 'flag' => array('keytype' => 'string', 'defaultkey' => false));
            $dimension_len = count($dimensions);
            if (is_array($web_request_data_delimiters) && $dimension_len > 0 && $dimension_len <= count($web_request_data_delimiters)) {
                $level = 0;
                //有可能标注了包装符号:
                $box_pair = null;
                if(strlen($str) > 6 && $str[0]=='(' && $str[1]=='=' && ($str[4]==')' || $str[5]==')')){
                    $str_copy = &$str;
                    unset($str);
                    if($str_copy[5]==')'){
                        $box_pair = substr($str_copy,2,3);
                        $str = substr($str_copy,6);
                    }
                    else{
                        $box_pair = substr($str_copy,2,2);
                        $str = substr($str_copy,5);
                    }
                    unset($str_copy);
                }
                $works = array(&$str);
                $subkeys = array_keys($dimensions);
                $subkey_fors = count($subkeys);
                for ($k = 0; $k < $subkey_fors; $k++) {
                    $dimension = &$dimensions[$subkeys[$k]];
                    $opt_key = &$subkeys[$k];
                    $onlyvalue = false;
                    if(isset($dimension['onlyvalue']) && boolval($dimension['onlyvalue'])){
                        $onlyvalue = true;
                    }
                    $level++;
                    $work_tasks = &get_node_for_level($works, $level);
                    foreach ($work_tasks as &$work) {
                        $exp_lx = &explode_chr_head_tail($work, $web_request_data_delimiters[$level],null,null,$box_pair);//pid级
                        if (!empty($exp_lx)) {
                            $work = array();
                            foreach ($exp_lx as &$tfexp) {
                                //JSON格式优先:
                                if ($tfexp[0] == '{' && $tfexp[strlen($tfexp) - 1] == '}') {
                                    $tfexp = xn_json_decode($tfexp);
                                }
                                else {
                                    $mh1 = false;
                                    if (!$onlyvalue)
                                        $mh1 = strpos($tfexp, $web_request_data_delimiters[0], 1);//flag-cmd组
                                    else {
                                        $work[] = &$tfexp;
                                        continue;
                                    }
                                    $cur_key = null;
                                    $cur_val = &$tfexp;
                                    if ($mh1 > 0) {
                                        $w_key = substr($tfexp, 0, $mh1);
                                        $x_key = trim($w_key);
                                        switch ($dimension['keytype']) {
                                            case 'int':
                                                if (is_numeric($x_key)) {
                                                    $cur_key = (int)$x_key;
                                                    $cur_val = trim(substr($tfexp, $mh1 + 1));
                                                }
                                                break;
                                            case 'float':
                                                if (is_numeric($x_key)) {
                                                    $cur_key = (float)$x_key;
                                                    $cur_val = trim(substr($tfexp, $mh1 + 1));
                                                }
                                                break;
                                            default:
                                                $cur_key = $x_key;
                                                $cur_val = trim(substr($tfexp, $mh1 + 1));
                                        }
                                    } else {
                                        if (isset($dimension['defaultkey']))
                                            $cur_key = &$dimension['defaultkey'];
                                    }
                                    if ($cur_key != null) {
                                        if (empty($cur_val) && isset($dimension['defaultvalue'])) {
                                            $cur_val = &$dimension['defaultvalue'];
                                            if ($cur_val === false) $cur_val = null;
                                        }
                                        if ($cur_val !== null) {
                                            if(!isset($dimension['namekey']) && !isset($dimension['nameval']))
                                                $work[$cur_key] = &$cur_val;
                                            else{
                                                $cur_node = array();
                                                if(isset($dimension['namekey']) && !empty($dimension['namekey']))
                                                    $cur_node[$dimension['namekey']] = &$cur_key;
                                                if(isset($dimension['nameval']) && !empty($dimension['nameval']))
                                                    $cur_node[$dimension['nameval']] = &$cur_val;
                                                $work[$cur_key] = &$cur_node;
                                                unset($cur_node);
                                            }
                                        }
                                    }
                                    unset($cur_key, $cur_val);
                                }
                            }
                        }
                    }
                    unset($work, $work_tasks, $opt_key, $dimension);
                }
                $ret = &$str;
            }
        }
    }
    if (!empty($ret) && is_array($params)) {
        $nokey = false;
        if (!empty($dimensions)) {
            $dimension = current($dimensions);
            if (!isset($dimension['key']) || boolval($dimension['key']) == false) {
                $nokey = true;
            }
        }
        foreach ($ret as $rk => &$rv) {
            if (!$nokey) {
                if(!isset($params[$rk]))
                    $params[$rk] = &$rv;
                else{
                    if(!is_array($params[$rk])){
                        $prk = &$params[$rk];
                        unset($params[$rk]);
                        $params[$rk] = array(&$prk);
                        unset($prk);
                    }
                    $params[$rk][] = &$rv;
                }
            } else
                $params[] = &$rv;
        }
    }
    return $ret;
}

/**
 * 递归处理数组中的数值值数组为字符串,主要在ConfigSerializationPreForThreadext()中使用
 * @param array $Arr
 * @param string $glue
 * @param array $exclude_keys 需要队的键名
 */
function _recursive_Arr(array &$Arr,$glue=',',array &$exclude_keys=null){
    if(!empty($Arr)){
        foreach($Arr as $k => &$v){
            if(!is_array($v) || empty($v) || ($exclude_keys!= null && in_array($k,$exclude_keys))) continue;
            $bk = false;
            $i = 0;
            foreach($v as $k2 => &$v2){
                if($k2 != $i || is_array($v2) || !is_numeric($v2)){
                    $bk = true;
                    break;
                }
                $i++;
            }
            if(!$bk){
                $v = implode($glue,$v);
            }
            else{
                _recursive_Arr($v,$glue,$exclude_keys);
            }
        }
    }
}
/**
 * 保存配置文件前运行的函数
 * @param array &$data 配置数组
 * @param mixed $dir 配置文件的目录,暂时没有使用
 * @return array
 */
function &ConfigSerializationPreForThreadext(array &$data,$dir){
    if(!empty($data)){
        $ret = $data;
        $exclude_keys = array('ExtendedData');
        _recursive_Arr($ret,',',$exclude_keys);
        //PostFlag的默认值处理:
        if(isset($ret['PostFlag']) && isset($ret['PostFlag']['Flags']) && isset($ret['PostFlag']['FlagControlDefault'])){
            $pfcds = &$ret['PostFlag']['FlagControlDefault'];
            foreach($ret['PostFlag']['Flags'] as $flag => &$fv){
                $ftype = &$fv['type'];
                $fvk = array_keys($fv);
                foreach($fvk as $k){
                    if(isset($pfcds[$k]) && isset($pfcds[$k][$ftype])){
                        if($fv[$k] == $pfcds[$k][$ftype]){
                            unset($fv[$k]);
                        }
                    }
                }
                unset($ftype);
            }
        }
        //Label/SubLabellog的处理
        if(isset($ret['Label']) && isset($ret['Label']['SubLabellog'])){
            foreach($ret['Label']['SubLabellog'] as &$sv){
                if(is_numeric($sv))  $sv = 'max_'.$sv;
            }
        }
        //Label/RelatedLinkTemplate的处理
        if(isset($ret['Label']) && isset($ret['Label']['RelatedLinkTemplate'])){
            if(!is_numeric(key($ret['Label']['RelatedLinkTemplate']))){
                $flipr = array_flip($ret['Label']['RelatedLinkTemplate']);
                if(!empty($flipr)){
                    $ret['Label']['RelatedLinkTemplate'] = &$flipr;
                }
            }
        }
    }
    else
        $ret = &$data;
    return $ret;
}


/**
 * 获取信息的多层分类和拥有者
 * 返回数组,层级由低到高排列,同时具有字符串Key与数字索引,第一个元素是uid,即信息的拥有者
 * TX_INFO_TYPE_POST可能包含有quotes,为一个数组节点,序号为0,层级由低到高排列
 * TX_INFO_TYPE_ATTACH的结果层级排列:pid/tid
 * @param int|string $type_table 信息类型代号TX_INFO_TYPE_xxx或者表名(不含前缀)
 * @param int|string $id 主键值
 * @param callable $callfun 用户函数,优先,并且不再运行本函数代码(除get_info_catalog_end之外);会传入参数:$type_table,$id,&$result_olds,&$own_user
 * @return array
 */
function &GetInfoCatalogWithOwn($type_table, $id, $callfun=null){
    static $result_olds = array();//一次请求内有多个相同的信息是不可避免的
    $ret = array();
    if($type_table != 0 && !empty($id)){
        if(is_string($type_table)){
            if(!is_numeric($type_table))
                $type_table = strtolower($type_table);
            else
                $type_table = intval($type_table);
        }
        $ckey = strval($type_table).','.strval($id);
        if(!empty($callfun) && is_callable($callfun)){
            $ret = call_user_func_array($callfun,array(&$type_table,$id,&$result_olds));
        }
        else{
            if(isset($result_olds[$ckey]))
                return $result_olds[$ckey];
            $cols = null;
            switch ($type_table){
                case TX_INFO_TYPE_POST:
                case 'post':
                    $cols = array('tid','quotepid','uid');
                    $dbr = db_find_one('post',array('pid'=>$id),array(),$cols);
                    if(empty($dbr)) break;
                    if(!empty($dbr['quotepid'])){
                        $qid = intval($dbr['quotepid']);
                        $quotes = array();
                        $level = 0;
                        $quotes['pid'.$level] = $qid;
                        $quotes[] = &$quotes['pid'.$level];
                        do{
                            $level++;
                            $dbr = db_find_one('post',array('pid'=>$qid),array(),array('quotepid'));
                            if(!empty($dbr)){
                                $qid = intval($dbr['quotepid']);
                                if(in_array($qid,$quotes,true))
                                    break;//有死循环,跳出
                                $quotes['pid'.$level] = $qid;
                                $quotes[] = &$quotes['pid'.$level];
                            }
                        }while(!empty($dbr) && $level<512);//最多512层
                        $ret['quotes'] = &$quotes;
                        $ret[] = &$quotes;
                        unset($quotes);
                    }
                    $id = intval($dbr['tid']);
                    $ret['uid'] = intval($dbr['uid']);
                    $ret['tid'] = $id;
                    $ret[] = &$ret['tid'];
                case TX_INFO_TYPE_THREAD:
                case 'thread':
                    $cols = array('fid','uid');
                    $dbr = db_find_one('thread',array('tid'=>$id),array(),$cols);
                    if(empty($dbr)) break;
                    $ret['uid'] = intval($dbr['uid']);
                    $id = intval($dbr['fid']);
                    $ret['fid'] = $id;
                    $ret[] = &$ret['fid'];
                case TX_INFO_TYPE_FORUM:
                case 'forum':
                    $forumcols = &GhxCache::getInstance()->get_share_var(VAR_TABLE_COLUMNS,'forum');
                    if(isset($forumcols['fup'])){
                        $cols = array('fup','moduids');
                        $dbr = db_find_one('forum',array('fid'=>$id),array(),$cols);
                        if(!empty($dbr)){
                            if(!empty($dbr['moduids'])){
                                $ret['uid'] = explode(',', $dbr['moduids']);
                                foreach($ret['uid'] as &$fuid)
                                    $fuid = intval($fuid);
                            }
                            else
                                $ret['uid'] = null;
                            $ret['fup'] = intval($dbr['fup']);
                            $ret[] = &$ret['fup'];
                        }
                    }
                    unset($forumcols);
                    break;
                case TX_INFO_TYPE_USER:
                case 'user':
                    $dbr = db_find_one('user',array('uid'=>$id),array(),array('gid'));
                    if(!empty($dbr)){
                        $ret['uid'] = $id;
                        $ret['gid'] = intval($dbr['gid']);
                        $ret[] = &$ret['gid'];
                    }
                    break;
                case TX_INFO_TYPE_ATTACH:
                case 'attach':
                    $cols = array('pid','tid','uid');
                    $dbr = db_find_one('attach',array('aid'=>$id),array(),$cols);
                    if(!empty($dbr)){
                        $ret['uid'] = intval($dbr['uid']);
                        $ret['pid'] = intval($dbr['pid']);
                        $ret[] = &$ret['pid'];
                        $ret['tid'] = intval($dbr['tid']);
                        $ret[] = &$ret['tid'];
                    }
                    break;
                default:
                    break;
            }
        }
        // hook get_info_catalog_end.php
    }
    $result_olds[$ckey] = &$ret;
    return $ret;
}

/**
 * param的包装版本,如果不存在则返回NULL
 * @param $key
 * @param bool $htmlspecialchars
 * @param bool $addslashes
 * @return array|int|mixed|string|null
 */
function param_null($key, $htmlspecialchars = TRUE, $addslashes = FALSE){
    $ret = param($key, "\v\r\f", $htmlspecialchars, $addslashes);
    if($ret == "\v\r\f")
        $ret = NULL;
    return $ret;
}

/**
 * 是否开启标题图片功能
 * @param array|null $titleImgCfg Cfg['TitleImage']的副本,不需要时不要传参,可以输出
 * @param string $curRoute
 * @return bool
 */
function IsEnableTitleImg(array &$titleImgCfg = null,$curRoute=null)
{
    global $route;
    static $EnableTitleImgCache = array();
    $TitleImgEnable = false;
    if($curRoute === null) $curRoute = $route;
    if($curRoute === '') $curRoute = '/';
    if($titleImgCfg == null) {
        if(isset($EnableTitleImgCache[$curRoute]))
            return $EnableTitleImgCache[$curRoute];
        $titleImgCfg = GetPluginCfgNode('TitleImage');
        $EnableTitleImgCache[$curRoute] = &$TitleImgEnable;//缓存结果
    }
    if (!empty($titleImgCfg) && !empty($titleImgCfg['enable'])) {
        $TitleImgEnable = true;
        if(isset($titleImgCfg['DeniedTitleImgRoutes']) && !empty($titleImgCfg['DeniedTitleImgRoutes'])){
            if(is_string($titleImgCfg['DeniedTitleImgRoutes'])) {
                $titleImgCfg['DeniedTitleImgRoutes'] = preg_split('/[\s,;\|]+/u',$titleImgCfg['DeniedTitleImgRoutes']);
            }
            if(is_array($titleImgCfg['DeniedTitleImgRoutes'])){
                if(in_array($curRoute,$titleImgCfg['DeniedTitleImgRoutes']))
                    $TitleImgEnable = false;
            }
        }
    }
    return $TitleImgEnable;
}

/**
 * 标记全局变量PostFlagEnabled
 * 会创建全局变量:$PostFlagEnabled,$PostFlagCfg,$PostFlagTable
 * @return bool 是否已经开启
 */
function GetPostFlagEnable()
{
    if (!isset($GLOBALS['PostFlagEnabled'])) {
        $GLOBALS['PostFlagCfg'] = &GetPostFlagCfg();
        $GLOBALS['PostFlagEnabled'] = $GLOBALS['PostFlagCfg']['enable'];
        $GLOBALS['PostFlagTable'] = $GLOBALS['PostFlagCfg']['table'];
        if ($GLOBALS['PostFlagEnabled'] < 1) unset($GLOBALS['PostFlagCfg'], $PostFlagCfg);
    }
    return $GLOBALS['PostFlagEnabled'];
}

/**
 * 获取主题列表上可显示的postflag列表,如果有缓存,从缓存读取
 * @param string|array $threadlistview 显示postflag列表的规则
 * @return array|null
 */
function &GetThreadListViewPostFlags(&$threadlistview)
{
    global $ghx_cache;
    $ret = null;
    if (GetPostFlagEnable()) {
        $tl_PostFlagCfg = &$GLOBALS['PostFlagCfg'];
        if (empty($threadlistview)) {
            $threadlistview = $tl_PostFlagCfg['threadlistview'];
        }
        $crc = 0;
        if (is_string($threadlistview)) {
            $crc = crc32($threadlistview);
        } elseif (is_array($threadlistview)) {
            $crc = crc32(implode(',', $threadlistview));
        }
        $ckey = 'ThreadListViewPostFlags' . $crc;
        $retobj = $ghx_cache->$ckey;
        if (!empty($retobj)) {
            $ret = $retobj->Val;
        }
        if ($ret === null) {
            if (is_string($threadlistview))
                $threadlistview = &explode_chr_head_tail($threadlistview, ',');
            $tl_flags = &$tl_PostFlagCfg['Flags'];
            $enable_flags = array();
            foreach ($threadlistview as $_tlv) {
                $_tlv = trim($_tlv);
                if (isset($tl_flags[$_tlv]) && $tl_flags[$_tlv]['enable']) $enable_flags[] = $_tlv;
                elseif ($_tlv[0] == ':' || $_tlv[strlen($_tlv) - 1] == ':') {
                    $_fgn = trim($_tlv, ':');
                    foreach ($tl_flags as $_flagName => &$_flagv) {
                        if ($_fgn == $_flagv['type'] && !in_array($_flagName, $enable_flags) && $_flagv['enable']) {
                            $enable_flags[] = $_flagName;
                        }
                    }
                }
            }
            $cfg_path = $ghx_cache->get_share_var(VAR_GHXTD_CFGPATH);
            $ghx_cache->$ckey = new CacheAtom($ghx_cache->CachePrefix . $ckey, $enable_flags, 3600*24*10, $cfg_path);
            $ret = &$enable_flags;
        }
    }
    return $ret;
}

/**
 * 解析PostFlag需要加载的Css,生成HTML<link>代码
 * @param string|array &$cssHref
 * @param string $template
 * @return string
 */
function &GetPostFlagStylesheet(&$cssHref,$template='<link rel="stylesheet" href="{path}">'){
    $link_stylesheet_code = '';
    if(empty($cssHref)){
        if (GetPostFlagEnable()) {
            $PostFlagCfg = &$GLOBALS['PostFlagCfg'];
            if (isset($PostFlagCfg['FlagControlDefault']['CssHref']))
                $cssHref = $PostFlagCfg['FlagControlDefault']['CssHref'];
        }
        if (!empty($cssHref)) {
            if (is_string($cssHref)){
                $cssHref = &explode_chr_head_tail($cssHref, ';');
            }
            foreach ($cssHref as &$link_stylesheet) {
                $link_stylesheet_code .= str_ireplace('{path}',$link_stylesheet,$template). PHP_EOL;
            }
        }
    }
    return $link_stylesheet_code;
}
/**
 * 获取PostFlag的配置信息
 * @param bool $refresh 是否强制刷新,默认为false
 * @return bool|array
 */
function &GetPostFlagCfg($refresh = false)
{
    $postFlagCfg = null;
    $pluginCfg = &get_plugin_config(GhxCache::getInstance()->get_share_var(VAR_GHXTD), $refresh);
    if (!empty($pluginCfg)){
        $postFlagCfg = &$pluginCfg['PostFlag'];
    }
    return $postFlagCfg;
}

/**
 * 获取指定操作有效的有权标记列表
 * @param string $type 类型,Auth或者Display
 * @param int $gid 只返回指定用户组有使用权限的标记数组,默认为0,表示从上下文获取$gid
 * @param bool $user_self 是否为内容创作者本人,默认为false
 * @param bool $refresh 是否强制刷新,默认为false
 * @return array|false
 */
function &ListValidPostFlags(string $type,int $gid=0,bool $user_self=false,bool $refresh = false){
    $ret = false;
    static $PostFlagsForGid = array();
    static $optype = array('Auth'=>'AuthGroup','Display'=>'DisplayGroup');
    if(!empty($type) && isset($optype[$type])) {
        if ($gid == 0) $gid = $GLOBALS['gid'];
        $ckey = $type.','.(string)$gid.($user_self?'':'!');
        if (!$refresh && !empty($PostFlagsForGid[$ckey]))
            $ret = &$PostFlagsForGid[$ckey];
        else {
            $postFlagCfg = &GetPostFlagCfg($refresh);
            if (!empty($postFlagCfg) && isset($postFlagCfg['Flags']) && (bool)$postFlagCfg['enable']) {
                $ret = array();
                foreach ($postFlagCfg['Flags'] as $pfK => &$pfV) {
                    if (boolval($pfV['enable']) && (empty($pfV[$optype[$type]]) || in_array($gid, $pfV[$optype[$type]], true) || ($user_self && in_array(USERSELF, $pfV[$optype[$type]], true))))
                        $ret[$pfK] = $pfV;//不要使用引用,无法控制
                }
                $PostFlagsForGid[$ckey] = &$ret;
            }
        }
    }
    return $ret;
}

/**
 * 获取指定Type的PostFlagCfg,并且以Group分组
 * @param string $type Flags/Flag/Type节点的值
 * @return array|null
 */
function &GetPostFlagCfgForType(string $type)
{
    global $ghx_cache;
    $ckey = 'PostFlagCfgForType';
    $ret = $PostFlagCfgG = null;
    $retobj = $ghx_cache->PostFlagCfgForType;
    if(!empty($retobj)){
        $PostFlagCfgG = $retobj->Val;
        if(!empty($PostFlagCfgG)){
            $ret = &$PostFlagCfgG[$type];
        }
    }
    if($ret === null && empty($PostFlagCfgG)){
        $PostFlagCfg = &GetPostFlagCfg(false);
        $PostFlagsCfg = null;
        if (isset($PostFlagCfg['Flags']))
            $PostFlagsCfg = &$PostFlagCfg['Flags'];
        if (!empty($PostFlagsCfg)){
            $cfg_path = $ghx_cache->get_share_var(VAR_GHXTD_CFGPATH);
            $PostFlagCfgG = array();
            foreach ($PostFlagsCfg as $f => &$cv) {
                $t = $cv['type'];
                $g = $cv['Group'];
                if (!isset($PostFlagCfgG[$t])) $PostFlagCfgG[$t] = array();
                if (!isset($PostFlagCfgG[$t][$g])) $PostFlagCfgG[$t][$g] = array();
                $PostFlagCfgG[$t][$g][$f] =& $cv;
            }
            $ret = &$PostFlagCfgG[$type];
            $ghx_cache->PostFlagCfgForType = new CacheAtom($ghx_cache->CachePrefix.$ckey,$PostFlagCfgG,0,$cfg_path);
        }
    }
    return $ret;
}

/**
 * 获取指定用户组可用的帖子标志数组数据,第一级为标志分组号,第二级为实际数据
 * 在GetPostFlagCfgForType()的结果上进行权限筛选
 * @param int $gid 用户组号
 * @param string $flagType 标志分类,mark,manage之类
 * @param bool $ignoreEnable 是否忽略enable,默认为false
 * @return array
 */
function GetViewPostFlags(int $gid, string $flagType, $ignoreEnable = false)
{
    $PostFlagCfg = &GetPostFlagCfg(false);
    $retval = array();//以分组号为一级索引
    if ($PostFlagCfg['enable'] > 0 || $ignoreEnable) {
        $PostFlagTypeCfg = &GetPostFlagCfgForType($flagType);
        if (!empty($PostFlagTypeCfg)) {
            foreach ($PostFlagTypeCfg as $flagGroup => &$flagGVal) {
                if (!isset($retval[$flagGroup])) $retval[$flagGroup] = array();
                foreach ($flagGVal as $flagName => &$flagVal) {
                    if ((!isset($flagVal['enable']) || $flagVal['enable'] > 0 || $ignoreEnable)) {
                        if (empty($flagVal['AuthGroup']) || in_array($gid, $flagVal['AuthGroup'], true)) {
                            if (!isset($retval[$flagGroup])) $retval[$flagGroup] = array();
                            $retval[$flagGroup][$flagName] = $flagVal;//复制数组
                        }
                    }
                }
            }
        }
    }
    return $retval;
}

/**
 * 获取插件配置子节点的值
 * @param string $node 子节点表达式,可以用任意分隔符分开,类似于xpath的分隔符格式
 * @param string|null $plugin 插件名字,默认为ghx_threadext
 * @param mixed|null $default 没有取到值时的替代值
 * @return mixed|null 值或者null,参数错误,一定返回null,而不是默认值
 */
function &GetPluginCfgNode(string $node, $plugin = null, $default = null)
{
    $ret = null;
    if (empty($node)) return $ret;
    if (empty($plugin)) {
        $plugin = GhxCache::getInstance()->get_share_var(VAR_GHXTD);
    }
    $pluginCfg = &get_plugin_config($plugin);
    if (!empty($pluginCfg)) {
        $nodes = preg_split('#[\s\\\\\\/\\|]+#s', $node, -1, PREG_SPLIT_NO_EMPTY);
        $current_node = &$pluginCfg[$nodes[0]];
        while ($current_node !== null && next($nodes) !== false) {
            $tmp = &$current_node[current($nodes)];
            unset($current_node);
            $current_node = &$tmp;
            unset($tmp);
        }
        if ($current_node !== null)//防止后面引用脏数据
            $ret = &$current_node;
    }
    if ($ret === null && $default !== null)
        $ret = $default;
    return $ret;
}

/**
 * 是否具有使用文章扩展功能的权限
 * @param int $gid 要检测的user group
 * @return bool
 */
function IsThreadExtensionAuthGroup($gid)
{
    $teagrops = &GetPluginCfgNode('ThreadExtensionAuthGroup');
    if (!empty($teagrops)) {
        return in_array(intval($gid), $teagrops, true);
    }
    return false;
}

/**
 * 验证用户是否具有指定标记的使用权限
 * @param string $flag 标记名称
 * @param int $fid 版块ID
 * @param int $uid 用户ID,0表示从当前上下文获取
 * @param int $gid 用户组ID,0表示从当前上下文获取
 */
function IsPostFlagAuthGroup(string $flag,$fid,$uid=0,$gid=0){
    $ret = false;
    if(!empty($flag)){
        if((int)boolval($uid)+(int)boolval($gid) != 1){//不允许只缺省一个
            if($uid==0) $uid=$GLOBALS['uid'];
            if($gid==0) $uid=$GLOBALS['gid'];
        }
    }
}

/**
 * 是否具有调整帖子排序系数的权限(先必须要有ThreadExtensionAuthGroup)
 * @param int $gid 要检测的user group
 * @return bool
 */
function IsAdjustPostSortRankAuthGroup($gid)
{
    $teagrops = &GetPluginCfgNode('AdjustPostSortRankAuthGroup');
    if (!empty($teagrops)) {
        return in_array(intval($gid), $teagrops, true);
    }
    return false;
}

/**
 * 是否具有调整文章展示模板的权限(先必须要有ThreadExtensionAuthGroup)
 * @param int $gid 要检测的user group
 * @return bool
 */
function IsAdjustViewTemplateAuthGroup($gid)
{
	$tvtNode = &GetPluginCfgNode('ViewTemplate');
	if($tvtNode != NULL){
		if(isset($tvtNode['enable']) && $tvtNode['enable'] > 0) {
			if (!empty($tvtNode['AdjustAuthGroup'])) {
					return in_array(intval($gid), $tvtNode['AdjustAuthGroup'], true);
			}
		}
	}
    return false;
}

/**
 * 提取标题图片
 * @param int|string $tid 主题贴ID,主要用于生成图片文件名
 * @param string $firstimg 预定的第一候选图片
 * @param array $attach_imgs 附件图片数组
 * @return bool|string
 */
function pickup_title_pic($tid, $firstimg, $attach_imgs)
{
    global $conf;
    if (empty($tid)) $tid = 0;
    $firstimgIsLocal = false;
    $firstimg_exists = false;
    if (!empty($firstimg)) {
        $firstimg_exists = file_exists($firstimg);
        if ($firstimg_exists) {
            if (preg_match('#\bupload[\\\/]titleimg[\\\/]' . addslashes($tid) . '\b#i', $firstimg) > 0) {
                return $firstimg;
            }
        }
        $firstimgIsLocal = IsKindredHost($firstimg);
    }
    $pluginCfg = &get_plugin_config(GhxCache::getInstance()->get_share_var(VAR_GHXTD));
    $TitleImgCfg = $pluginCfg['TitleImage'];
    if (!empty($TitleImgCfg) && !empty($TitleImgCfg['enable'])) { //是否开启标题图片功能
        $imgheight = $imgwidth = 0;
        if (!empty($TitleImgCfg['Height'])) $imgheight = intval($TitleImgCfg['Height']);
        if (!empty($TitleImgCfg['Width'])) $imgwidth = intval($TitleImgCfg['Width']);
        if ($imgheight + $imgwidth < 10) $imgheight = 150;
        /** 标题图片 */
        $teimg1 = '';
        /** 标题图片的PATH */
        if(isset($TitleImgCfg['SavePath']) && !empty($TitleImgCfg['SavePath']))
            $teimg1path = $TitleImgCfg['SavePath'];
        else
            $teimg1path = $conf['upload_url'] . 'titleimg/';
        $teimg1path_endchr = $teimg1path[strlen($teimg1path)-1];
        if($teimg1path_endchr != '/' && $teimg1path_endchr != '\\')
            $teimg1path .= '/';
        unset($teimg1path_endchr);
        $my_host = $_SERVER['HTTP_HOST'];
        if (stripos($my_host, 'www.') === 0)
            $my_host = substr($my_host, 4);
        $imgRet1 = null;
        if (empty($firstimg) || ($firstimgIsLocal && !$firstimg_exists)) {
            /**文章内容*/
            $tecontent = '';
            //分析标题图片:
            if (!empty($attach_imgs) && is_array($attach_imgs))//在attach_assoc_post_end.php里设置
            {
                //上传的图片优先选用:_POST
                if (!empty($attach_imgs['images'])) {
                    foreach ($attach_imgs['images'] as &$attach_imgpos) {
                        if (!empty($attach_imgpos['filename'])) {
                            $teimg1 = (empty($attach_imgs['imgpath']) ? $teimg1path : $attach_imgs['imgpath']) . $attach_imgpos['filename'];
                        } else {
                            $teimg1 = $attach_imgpos['url'];
                            $urlmatch = array();
                            if (preg_match('#^https?\:\/\/(www\.)?' . preg_quote($my_host) . '\/(.+)$#i', $teimg1, $urlmatch) > 0) {
                                $teimg1 = $urlmatch[2];
                            }
                        }
                        if (file_exists($teimg1)) break;
                    }
                }
                if (!empty($attach_imgs['message_fmt'])) {
                    $tecontent = &$attach_imgs['message_fmt'];
                }
            } else //没有图片上传时,准备取文章内容中的第一张图片
            {
                $tecontent =& $_POST['message'];
            }
            $img_istmpfile = false;//是否为临时图片,需要删除的
            if ((empty($teimg1) || !file_exists($teimg1)) && !empty($tecontent))//取文章内容中的第一张图片
            {
                $timgReg = '#(<img\s+[^\>\<]*?\bclass\s*\=\s*(\'|")?[^\'"]*?\btitleimg\b[^\'"]*\\2[^\>\<]*?>)#isU';
                $timgReg2 = '#<img\s+.*?\bsrc\s*\=\s*(\'|")?([^\'"]+?)\\1[^\>]*>#isU';
                $timgRS = array();
                if (preg_match($timgReg, $tecontent, $timgRS) > 0 && preg_match($timgReg2, $timgRS[1], $timgRS) > 0) {
                    $teimg1 = $timgRS[2];
                    $imgRet1 = GetImgFromDomSrc($teimg1, $TitleImgCfg, $teimg1path . 'remote/', $tid, $imgheight, $imgwidth);
                }
                if (empty($imgRet1)) {
                    preg_match_all('#<img\s+.*>#isU', $tecontent, $ereg);//正则表达式把图片的整个都获取出来了
                    if (!empty($ereg)) {
                        $ereg = $ereg[0];
                        $hregex = '#(\bstyle\s*\=\s*(\'|")?[^\'"]*?\bheight\s*\:\s*([\d\.]+)\s*p[^\'"]+?\\3)|(\bheight\s*\=\s*(\'|")?([\d\.]+[^\'"]*?)\\5)#isU';
                        $wregex = '#(\bstyle\s*\=\s*(\'|")?[^\'"]*?\bwidth\s*\:\s*([\d\.]+)\s*p[^\'"]+?\\3)|(\bwidth\s*\=\s*(\'|")?([\d\.]+[^\'"]*?)\\5)#isU';
                        foreach ($ereg as $ev) {
                            if (preg_match('#\bsrc\s*\=\s*(\'|")?([^\'"\s]+)\\1#isU', $ev, $teimg1))//正则表达式
                            {
                                //获取style里的高度与宽度:
                                $styleh = null;
                                $stylew = null;
                                $mhrs = array();
                                $mwrs = array();
                                if (preg_match($hregex, $ev, $mhrs) > 0) {
                                    if (!empty($mhrs[6])) $styleh = floatval($mhrs[6]);
                                    else $styleh = floatval($mhrs[4]);
                                }
                                if (preg_match($wregex, $ev, $mwrs) > 0) {
                                    if (!empty($mwrs[6])) $stylew = floatval($mwrs[6]);
                                    else $stylew = floatval($mwrs[4]);
                                }
                                //把太小的图片用正式结果筛掉
                                if ($imgheight != 0 && $styleh < $imgheight * 1.5) continue;
                                if ($imgwidth != 0 && $stylew < $imgwidth * 1.5) continue;
                                $ev = $teimg1[2];//取出图片SRC
                                $imgRet1 = GetImgFromDomSrc($ev, $TitleImgCfg, $teimg1path . 'remote' . DIRECTORY_SEPARATOR, $tid, $imgheight, $imgwidth);
                                if (!empty($imgRet1)) break;
                            }
                        }
                    }
                }
            }
        } else {
            $imgRet1 = GetImgFromDomSrc($firstimg, $TitleImgCfg, $teimg1path . 'remote' . DIRECTORY_SEPARATOR, $tid, $imgheight, $imgwidth);
        }
        if (!empty($imgRet1)) {
            $teimg1 = $imgRet1['imgfile'];
            $img_istmpfile = $imgRet1['istmp'];
        }
        if (!empty($teimg1))//已经候选到图片:
        {
            $imgtypestr = $TitleImgCfg['ImageType'];
            $imgtypeval = IMAGETYPE_JPEG;
            if (!empty($imgtypestr)) {
                $imgtypestr = trim($imgtypestr, " \t\n\r\0\x0B*.?");
                $imgtypeval = extension_to_image_type($imgtypestr);
            }
            $save_imgpath = $teimg1path . strval($tid) . '.' . $imgtypestr;
            if (!dealthumb($teimg1, $imgheight, $imgwidth, $save_imgpath, $imgtypeval, $img_istmpfile)) //可能是没有保存成功的图片
            {
                $rmstr = 'remote' . DIRECTORY_SEPARATOR;
                $rmpos = stripos($teimg1, $rmstr, 3);
                if ($rmpos > 0 && ($teimg1[$rmpos - 1] == '\\' || $teimg1[$rmpos - 1] == '/')) {
                    $newfname = substr($teimg1, 0, $rmpos) . substr($teimg1, $rmpos + strlen($rmstr));
                    if (file_exists($teimg1) && (str_replace('/', DIRECTORY_SEPARATOR, $teimg1) == str_replace('/', DIRECTORY_SEPARATOR, $newfname) || @rename($teimg1, $newfname)))//直接从临时目录移动到目标目录
                        $firstimg = $newfname;
                } else
                    $firstimg = $teimg1;
            } else $firstimg = $save_imgpath;
            $firstimg2 = str_replace('/', DIRECTORY_SEPARATOR, $firstimg);
            $APPPATH = GhxCache::getInstance()->get_share_var(VAR_APPPATH);
            if (stripos($firstimg2, $APPPATH) === 0) {
                $firstimg = substr($firstimg, strlen($APPPATH) - 1);
            }
            return str_replace(DIRECTORY_SEPARATOR, '/', $firstimg);
        }

    }
    return false;
}

/**
 * 生成标题图片路径的功能实现
 * @param int|string $tid tid
 * @param int|string|null $alt_img_path 本地图片不存在时的代替图片,int为替代tid,null表示从数据库中读取
 * @param bool $detect_exist 检测本地图片是否存在,默认为否,如果需要检查,则前提条件是enable为1
 * @param string|null $path 图片路径,null表示为配置TitleImage:SavePath
 * @param string|null $img_ext 图片扩展名,null表示为配置TitleImage:ImageType
 * @return string
 */
function &GetTitleImageUrl($tid,$alt_img_path=0,bool $detect_exist=false,$path=null,$img_ext=null){
    static $cfg = null;
    global $ghx_cache,$conf;
    if($cfg === null && ($path == null || $img_ext == null || $detect_exist))
        $cfg = &GetPluginCfgNode('TitleImage');
    if($path == null){
        if(!empty($cfg['SavePath'])){
            $path = $cfg['SavePath'];
        }
        else{
            $path = $conf['upload_url'] . 'titleimg/';
        }
    }
    if($img_ext == null){
        $img_ext = trim($cfg['ImageType'], " \t\n\r\0\x0B*.?");
        if(empty($img_ext)) $img_ext = 'jpg';
    }
    $path_endchr = $path[strlen($path)-1];
    if($path_endchr != '/' && $path_endchr != '\\') $path .= DIRECTORY_SEPARATOR;
    $ret =  $path.$tid.'.'.$img_ext;
    if($detect_exist) {
        if($cfg['enable']) {
            if (!is_file($ret)) {
                if ($alt_img_path !== NULL) {
                    if (ctype_digit($alt_img_path)) {
                        $ret = $path . $alt_img_path . '.' . $img_ext;
                    } else {
                        $ret =  &$alt_img_path;
                    }
                } else {//需要从数据库中读取:
                    $theadext_table = $ghx_cache->get_share_var(VAR_THREADEXT_TB);
                    $dbimg = db_find_one($theadext_table, array('tid' => $tid), array(), array('tid', 'firstimg'));
                    if ($dbimg != null) {
                        $ret =  &$dbimg['firstimg'];
                    }
                }
            }
        }
        else
            $ret =  '';
    }
    if(!empty($ret)) {
        $ret = str_replace('\\', '/', $ret);
        $appath = $ghx_cache->get_share_var(VAR_APPPATH, '/');
        $appath_len = strlen($appath);
        if (substr($ret, 0, $appath_len) == $appath) {
            $ret = substr($ret, $appath_len);
        }
    }
    return $ret;
}

/**
 * 生成标题图片路径的统一入口
 * @param int|string $tid tid
 * @param int|string|null $alt_img_path 本地图片不存在时的代替图片,int为替代tid,null表示从数据库中读取
 * @param bool $detect_exist 检测本地图片是否存在,默认为否,如果需要检查,则前提条件是enable为1
 * @return string
 */
function &BuildTitleImageUrl($tid,$alt_img_path=0,bool $detect_exist=false){
    static $cfg = null;
    if($cfg === null) $cfg = &GetPluginCfgNode('TitleImage');
    $ret = '';
    if(isset($cfg['AssemblyUrlFun']) && is_callable($cfg['AssemblyUrlFun']) && $cfg['AssemblyUrlFun'] != 'GetTitleImageUrl'){
        $ret = call_user_func($cfg['AssemblyUrlFun'],$tid,$alt_img_path,$detect_exist);
    }
    else{
        $ret = &GetTitleImageUrl($tid,$alt_img_path,$detect_exist);
    }
    return $ret;
}

/**
 * 创建标签记录分表
 * @param $SubId 分表号
 * @return bool
 */
function CreateLabelSubtable($SubId)
{
    global $db, $tablepre, $conf;
    if (empty($tablepre)) $tablepre = $db->tablepre;//防止重新定义$tablepre
    $labelog_tb = GhxCache::getInstance()->get_share_var(VAR_LABELOG_TB) . strval($SubId);
    $sql = "CREATE TABLE IF NOT EXISTS `{$tablepre}{$labelog_tb}` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '标签记录ID' ,
`lid` INT NOT NULL COMMENT '标签ID' ,
`dist` INT NOT NULL COMMENT '目标代码' ,
`distid` INT NOT NULL COMMENT '目标ID' ,
`distclass` INT NULL DEFAULT '0' COMMENT '目标分类' ,
`disabled` TINYINT NULL DEFAULT '0' COMMENT '禁用',
`rank` INT NULL DEFAULT '0' COMMENT '排序系数' ,
`atime` INT NULL DEFAULT '0' COMMENT '记录时间' ,
`ztime` INT NULL DEFAULT '0' COMMENT '有效时间' ,
`info` VARCHAR(512) NULL DEFAULT '' COMMENT '标记说明' ,
`sign` INT NULL DEFAULT '0' COMMENT '标志位值' ,
`vint` INT NULL DEFAULT '0' COMMENT 'INT附值' ,
`vdouble` DOUBLE NULL DEFAULT '0' COMMENT 'DOUBLE附值' ,
`vstr` VARCHAR(50) NULL DEFAULT '' COMMENT '字符串附值' ,
PRIMARY KEY (`id`), 
KEY `labelrank` (`rank`),
UNIQUE KEY `labelrow` (`lid`, `dist`, `distid`,`sign`) USING BTREE,
KEY `labeltime` (`atime`,`ztime`,`disabled`),
KEY `labelatint` (`vint`),
KEY `labelatstr` (`vstr`),
KEY `distclass` (`distclass`)
) ENGINE = MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT = '标签记录表{$SubId}';";
    if ($conf['db']['type'] == 'sqlite' || $conf['db']['type'] == 'pdo_sqlite') $sql = &FixSqlFieldComment($sql, $conf['db']['type']);
    return db_exec($sql);
}

/**
 * 将dist表名转换为代码(引用传参,会改变其内部值)
 * @param mixed &$tableVar 是引用,会改变其内部值
 * @param string|null $fieldName 数组中对应的键名或者对象中对应的成员名
 * @return bool 是否正确转换
 */
function ConverseDistCode(&$tableVar, $fieldName = null)
{
    static $distcodeCfg = null;
    $ret = false;
    if (!empty($tableVar)) {
        if (is_numeric($tableVar))
            $tableVar = intval($tableVar);
        else {
            if ($distcodeCfg === null) {
                $distcodeCfg = &GetPluginCfgNode('Label/distcode');
            }
            if (!empty($distcodeCfg)) {
                if (is_string($tableVar)) {
                    if (isset($distcodeCfg[$tableVar]['id'])) {
                        $tableVar = $distcodeCfg[$tableVar]['id'];
                        $ret = true;
                    }
                } elseif (is_array($tableVar)) {
                    $test1 = current($tableVar);
                    if (is_string($test1)) {
                        foreach ($tableVar as &$item) {
                            if (isset($distcodeCfg[$item]['id'])) {
                                $item = $distcodeCfg[$item]['id'];
                                $ret = true;
                            }
                        }
                    } elseif (!empty($fieldName)) {
                        if (is_array($test1)) {
                            foreach ($tableVar as &$items) {
                                if (isset($items[$fieldName])) {
                                    if (isset($distcodeCfg[$items[$fieldName]]['id'])) {
                                        $items[$fieldName] = $distcodeCfg[$items[$fieldName]]['id'];
                                        $ret = true;
                                    }
                                }
                            }
                        } elseif (is_object($test1)) {
                            foreach ($tableVar as &$items) {
                                if (method_exists($items, $fieldName)) {
                                    if (isset($distcodeCfg[$items->$fieldName]['id'])) {
                                        $items->$fieldName = $distcodeCfg[$items->$fieldName]['id'];
                                        $ret = true;
                                    }
                                }
                            }
                        }
                    }
                } elseif (is_object($tableVar)) {
                    if (method_exists($tableVar, $fieldName)) {
                        if (isset($distcodeCfg[$tableVar->$fieldName]['id'])) {
                            $tableVar->$fieldName = $distcodeCfg[$tableVar->$fieldName]['id'];
                            $ret = true;
                        }
                    }
                }
            }
        }
    }
    return $ret;
}
/**
 * 由关键词队列的序号获取搜索匹配的加权指数
 * @param array &$LabelIds 关键词队列,格式:array(),或者array(array({'id'}|{'lid'}))
 * @param array &$SMSerialWeighteds 搜索匹配的加权指数表
 * @param $lid 要匹配的$lid
 * @param bool $detect_exist 是否检测$lid是否存在于$LabelIds
 * @return float|1|0 返回0为不存在
 */
function GetSearchMatchSerialWeighted(array &$LabelIds,array &$SMSerialWeighteds,$lid,$detect_exist=false){
    $ret = 1;
    if($lid>0 && !empty($LabelIds) && !empty($SMSerialWeighteds)){
        $lidpos = false;
        $test1 = current($LabelIds);
        $c = count($SMSerialWeighteds);
        if(is_array($test1)){
            $curPos = 0;
            foreach ($LabelIds as &$labelId){
                if(isset($labelId['id'])){
                    if($labelId['id'] == $lid)
                    {
                        $lidpos = $curPos;
                        break;
                    }
                }
                elseif(isset($labelId['lid'])){
                    if($labelId['lid'] == $lid)
                    {
                        $lidpos = $curPos;
                        break;
                    }
                }
                else break;//不符合规则,跳出
                if($curPos++ >= $c){
                    if(!$detect_exist)
                        $ret =  $SMSerialWeighteds[$c-1];
                    break;
                }
            }
        }
        else{
            $lidpos = array_search($lid,$LabelIds);
        }
        if($lidpos !== false){
            if($c > $lidpos)
                $ret = $SMSerialWeighteds[$lidpos];
            else
                $ret = $SMSerialWeighteds[$c-1];
        }
        elseif($detect_exist){
            $ret = 0;
        }
    }
    return $ret;
}

/**
 * 解析在关键文章列表中的排除项
 * @param string &$RelatedLinkStr 关键文章列表内容
 * @param string $custom_reg 自定义的匹配正则式,如果指定,则必须同时指定$custom_reggroup
 * @param string $custom_reggroup 自定义的正则式匹配组名与组位置,关系到返回数组的键名
 * @param string|array $mark_filter 指定特定的mark列表,没有设置或者非指定mark的不被返回,默认为空,意为全部
 * @return array|null 根据参数reggroup来返回,一般的输出格式是:array(array('mark'=>{1},'url'=>{2},'table'=>{4},'id'=>{5},'firstimg'=>{7},'title'=>{8},'info'=>{10})...) {n}中的数字在此不代表意义
 */
function &ParseRelatedLinkExclude(string &$RelatedLinkStr,string $custom_reg=null,array $custom_reggroup=null,$mark_filter=null)
{
    static $reg = null;
    static $regexgroup = null;

    $ret = null;
    if($custom_reg!=null && (empty($custom_reggroup) || count($custom_reggroup)<3))
        return $ret;
    if (!empty($RelatedLinkStr)) {
        if($reg === null){//初始化:
            $regCfg = &GetPluginCfgNode('Label/RelatedLinkTemplate');
            if(!empty($regCfg)){
                if(isset($regCfg['regex']))
                    $reg = &$regCfg['regex'];
                if(empty($reg)) {
                    $reg2 = "#^\\s*(.*?)\\s*\\[\\s*([^\\]\\s]+)\\s*\\]\\s*(\\<\\s*(\\w+)\\s*,\\s*(\\d+)(\\s*,\\s*([^\\>,]*?))?\\s*\\>)?\\s*([^\\v]+)?\\s*(\\v(.+))?$#su";
                    $reg = &$reg2;
                    unset($reg2);
                }
                $regexgroup = $regCfg['regexgroup'];
                if(empty($regexgroup)){
                    $regexgroup = array('mark'=>1,'url'=>2,'table'=>4,'id'=>5,'firstimg'=>7,'title'=>8,'info'=>10);
                }
                if(is_numeric(key($regexgroup))){
                    $regexgroup = array_flip($regexgroup);
                }
            }
        }
        if($custom_reg==null){
            $custom_reg = &$reg;
            $custom_reggroup = &$regexgroup;
            if(is_numeric(key($custom_reggroup))){
                $custom_reggroup = array_flip($custom_reggroup);
            }
        }
        $mark_filter_on = false;
        if(!empty($mark_filter) && isset($custom_reggroup['mark'])){
            if(is_string($mark_filter)) $mark_filter = preg_split('#[,;\t\n \\\\]+#s', $mark_filter, -1, PREG_SPLIT_NO_EMPTY);
            if(!empty($mark_filter)) $mark_filter_on = true;
        }
        $exMatches_t = null;
        if(preg_match_all('/(\&[\w]{2,10};|\&#\d+)/s',$RelatedLinkStr,$exMatches_t,PREG_SET_ORDER)>1){
            $RelatedLinkStr = html_entity_decode($RelatedLinkStr);
        }
        unset($exMatches_t);
        $ret = array();
        $RelatedLinkLines = array_unique(preg_split('#[\\r\\n]+#s',$RelatedLinkStr,-1,PREG_SPLIT_NO_EMPTY));
        foreach($RelatedLinkLines as &$line){
            $exMatch = null;
            if (preg_match($custom_reg, $line, $exMatch) > 0){
                if($mark_filter_on){
                    if(!in_array($exMatch[$custom_reggroup['mark']],$mark_filter))
                        continue;
                }
                $mr = array();
                foreach ($custom_reggroup as $gname => $gpos){
                    if(count($exMatch) > $gpos)
                        $mr[$gname] = $exMatch[$gpos];
                }
                $mr['RawLine'] = $exMatch[0];//保存原始文本
                $ret[] = &$mr;
                unset($mr);
            }
        }
        if (empty($ret)) $ret = null;
    }
    return $ret;
}

/**
 * 根据Label分表规则确定分表号
 * @param array &$SubLabellogs Label分表规则
 * @param int $id
 * @param boolean $autoCreateSubtable 是否自动创建分表,默认为false
 * @return int
 */
function GetSubtableId(array &$SubLabellogs, $id, $autoCreateSubtable = false)
{
    if (empty($SubLabellogs) || empty($id)) return 0;
    $cur = -1;
    $max = -1;
    foreach ($SubLabellogs as $k => $v) {
        if ($v >= $id) {
            $cur = $k;
            break;
        }
        $max = $v;
    }
    if ($cur == -1) {//已经超过最大值
        end($SubLabellogs);
        $cur = key($SubLabellogs) + 1;
    }
    if ($autoCreateSubtable && $max == $id - 1) {
        CreateLabelSubtable($cur);
    }
    return $cur;
}

/**
 * 将字符串转变为LabelId
 * @param string|null $keywords 关键词字符串,可以用常见字符分隔,但是中文标点不视为分隔符
 * @param string $LabelCate labelcate的title,Label的类型值,可以为int,即label的cate字段
 * @param bool $autoSave 是否自动保存数据表中不存在的标签/关键词
 * @param mixed $getRank 是否设置关键词权重,默认为-1检测配置,false:否,true:是,-1:检测(一行关键词,从左到右,权重值从大到小),不存在则为[2,1]
 * @return array|false 返回false为错误,格式:array({name}=>array({id},{name},{subtable},{cate})) cate可能不存在
 */
function &StrToLabel($keywords, $LabelCate = 'thread', $autoSave = true, $getRank = -1)
{
    $ret = false;
    if (!empty($keywords) && is_string($keywords)) {
        $keywords = preg_split('#[,;\|\t\n\\\/]+|\s{2,}#us', $keywords, -1, PREG_SPLIT_NO_EMPTY);
        foreach($keywords as &$_kw_trim){
			$_kw_trim = trim($_kw_trim);
		}
		$keywords = array_unique($keywords);
        $cols = array('id', 'name', 'subtable');
        $cond = array('name' => $keywords);
        if ($autoSave && !empty($LabelCate)) array_push($cols, 'cate');
        $kwlen = count($keywords);
        $labtn = GhxCache::getInstance()->get_share_var(VAR_LABEL_TB);
        $ret = db_find($labtn, $cond, array(), 1, $kwlen, 'name', $cols);
        if ($ret !== false) {
            $retLen = count($ret);
            if ($retLen != $kwlen && $autoSave) {
                $LabelCfg = &GetPluginCfgNode('Label');//引用更快
                if (!empty($LabelCfg)) {
                    $updcate = array();
                    $lcno = 0;
                    if (!empty($LabelCate) && $autoSave) {
                        if (is_numeric($LabelCate))
                            $lcno = intval($LabelCate);
                        else {
                            if (isset($LabelCfg['labelcate'][$LabelCate]))
                                $lcno = intval($LabelCfg['labelcate'][$LabelCate]['value']);
                        }
                        if ($lcno > 0) {
                            foreach ($ret as $lname => &$rowd) {
                                if (($rowd['cate'] & $lcno) == 0) {
                                    $updcate[] = $rowd['id'];//标记更新
                                    $rowd['cate'] |= $lcno;//更新查询结果
                                }
                            }
                        }
                    }
                    if (!empty($updcate)) {//更新cate
                        global $tablepre;
                        $sql = "UPDATE `{$tablepre}{$labtn}` SET `cate`=`cate`|{$lcno} WHERE id IN(" . implode(',', $updcate) . ')';
                        db_exec($sql);
                    }

                    $haved = array_keys($ret);
                    $adds = array_diff($keywords, $haved);
                    foreach ($adds as &$a) {
                        $newArr = array('name' => &$a);
                        if (is_numeric($LabelCate))
                            $cate = intval($LabelCate);
                        else {
                            if (isset($LabelCfg['labelcate'][$LabelCate]))
                                $cate = intval($LabelCfg['labelcate'][$LabelCate]['value']);
                        }
                        if ($cate != 0) $newArr['cate'] = $cate;
                        $newId = db_insert($labtn, $newArr);
                        if ($newId !== false) {
                            $newArr['id'] = intval($newId);
                            $newArr['subtable'] = 0;
                            $subId = GetSubtableId($LabelCfg['SubLabellog'], $newArr['id'], $autoSave);
                            if ($subId != 0) {//更新分表号:
                                if (db_update($labtn, array('id' => $newId), array('subtable' => $subId)) !== false)
                                    $newArr['subtable'] = $subId;
                            }
                            //插入到查询结果:
                            $newArr2 = array('id' => $newArr['id'], 'name' => $newArr['name'], 'subtable' => $newArr['subtable']);
                            if (isset($newArr['cate'])) $newArr2['cate'] = $newArr['cate'];
                            $ret[$a] = $newArr2;
                        }
                    }
                }
            }
            $ArrangementWeightsCfg = null;
            if ($getRank == -1) {
                $ArrangementWeightsCfg = &GetPluginCfgNode('ArticleKeyWord/ArrangementWeights');
                if ($ArrangementWeightsCfg != null && !empty($ArrangementWeightsCfg))
                    $getRank = true;
                else
                    $getRank = false;
            }
            if ($getRank == true && $retLen > 0) {
                if ($ArrangementWeightsCfg === null)
                    $ArrangementWeightsCfg = &GetPluginCfgNode('ArticleKeyWord/ArrangementWeights');//引用更快
                if (empty($ArrangementWeightsCfg)) {
                    $ArrangementWeightsCfg = array(2, 1);
                }
                $awLen = count($ArrangementWeightsCfg);
                $lastAW = $ArrangementWeightsCfg[$awLen - 1];
                foreach ($keywords as $k => &$v) {
                    $ref = &$ret[$v];
                    if ($ref != null && !isset($ref['rank'])) {//已经设置的不覆盖
                        if ($k < $awLen)
                            $ref['rank'] = $ArrangementWeightsCfg[$k];
                        else
                            $ref['rank'] = $lastAW;
                    }
                    unset($ref);
                }
            }
            //按原序排列:
            $ret2 = array();
            foreach ($keywords as $k => &$v) {
                if (isset($ret[$v])) {
                    $ret2[$v] = &$ret[$v];
                }
            }
            $ret = &$ret2;
        }
    }
    else{
        if($keywords==='')
            $ret = array();
        else{
            if(is_array($keywords)){
                $test1 = current($keywords);
                if((is_array($test1) && isset($test1['id'])) || (empty($keywords)))
                    $ret = &$keywords;
            }
        }
    }


    return $ret;
}



/**
 * 添加标签记录(支持批量)
 * @param array &$labelogs 标签记录数据或其多条数据组成的数组,必须包含键值dist,distid,lid,(dist,distid可以在其后的两个参数中统一指定)其中:dist一般为标签对象的表名代号(可以为表名字符串),lid为标签ID数据(如果分表号不是0,此项可以是array({标签数据集,必须有{id}和{subtable}})),也可以为标签字符串(警告:标签字符串的类型被处理为$table,如果非此,在调用前手动把标签转换为标签ID)
 * @param int $dataID 标签目标的数据ID,一般为表的ID,如果$labelogs看缺省distid,则用此代替
 * @param mixed|string $table 标签目标,即被贴标签的表名,也可以为表代码
 * @param array|null &$extended_fields $labelogs中没有明确指定的其它字段值的设定,一行则共用,多行则与$labelogs的每行对应,如果传入纵向数组,则会通过ArrayMatrixConversion()转换为横向
 * @return array 返回成功记录的数组,格式:array({newId}=>array({$labelog}))
 */
function AddLabelLog(array &$labelogs, int $dataID = 0, $table = 'thread',array &$extended_fields=null)
{
    global $ghx_cache;
    $labellogTB = $ghx_cache->get_share_var(VAR_LABELOG_TB);
    $fields = $ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $labellogTB . '0');
    unset($fields['id']);//不允许指定ID

    $ret = array();
    $LabelCfg = null;
    $tableId = $table;
    if (!empty($labelogs)) {
        $test1 = current($labelogs);
        $optArr = null;
        if (!is_array($test1) || isset($labelogs['lid'])) {
            $optArr = array(&$labelogs);
        } else {
            $optArr = &$labelogs;
        }

        $cur_extended_fields = null;
        $extended_fields_count = 0;
        //处理$extended_fields:
        if(!empty($extended_fields)) {
            $extended_fields2 = &ArrayMatrixConversion($extended_fields, true);
            if ($extended_fields2 !== false) {
                $extended_fields = &$extended_fields2;
            }
            unset($extended_fields2);
            $extended_fields_count = 1;
            if(is_array(current($extended_fields))){
                $extended_fields_count = count($extended_fields);
            }
        }
        $Attach = array();
        $inserted = array();
        foreach ($optArr as &$curp) {
            if (!empty($extended_fields)) {
                if ($cur_extended_fields === null) {
                    if (is_array(current($extended_fields)))
                        $cur_extended_fields = current($extended_fields);
                    else
                        $cur_extended_fields = $extended_fields;
                } elseif ($cur_extended_fields !== false && $extended_fields_count > 1)
                    $cur_extended_fields = next($extended_fields);
            }
            if($cur_extended_fields != null)
                $insdata = array_merge($cur_extended_fields,$curp);//$labelogs的设置优先
            else $insdata = $curp;
            if (!isset($insdata['dist'])) $insdata['dist'] = $tableId;
            if (!is_numeric($insdata['dist'])) {
                if ($LabelCfg === null) $LabelCfg = &GetPluginCfgNode('Label');
                if (!empty($LabelCfg['distcode']) && isset($LabelCfg['distcode'][$table]['id'])) {
                    $insdata['dist'] = intval($LabelCfg['distcode'][$insdata['dist']]['id']);
                    if (is_string($tableId)) {
                        $tableId = $insdata['dist'];
                    }
                } else
                    $insdata['dist'] = 0;
            }
            if ($insdata['dist'] == 0) continue;
            if (empty($insdata['distid'])) {
                if ($dataID != 0)
                    $insdata['distid'] = $dataID;
                else continue;
            }
            if (!isset($insdata['atime'])) $insdata['atime'] = time();//设置默认时间
            $insdata = array_intersect_key($insdata, $fields);//过滤无效字段
            if (!empty($insdata) && isset($insdata['dist']) && !empty($insdata['lid'])) {
                if (!is_array($insdata['lid'])) {
                    $LabelId = null;
                    if (!is_numeric($insdata['lid'])) {
                        $LabelIds = StrToLabel($insdata['lid'], $table, true,true);
                        if (!empty($LabelIds)) {
                            $LabelId = current($LabelIds);
                        }
                        if (count($LabelIds) > 1) {
                            while (next($LabelIds) !== false) {
                                $insdata['lid'] = current($LabelIds);
                                $Attach[] = $insdata;
                            }
                        }
                        $insdata['lid'] = $LabelId;
                    } else {//只有ID,没有分表号:
                        if (is_string($insdata['lid'])) $insdata['lid'] = intval($insdata['lid']);
                        $subtbId = GetSubtableId($LabelCfg['SubLabellog'], $insdata['lid'], false);
                        $insdata['lid'] = array('id' => $insdata['lid'], 'subtable' => $subtbId);
                    }
                }
                else{
                    if (is_array(current($insdata['lid']))){//多条合并成一组,需要拆解:
                        $chaijie = &$insdata['lid'];
                        unset($insdata['lid']);//解除联系,必须!
                        while (next($chaijie) !== false) {
                            $insdata['lid'] = current($chaijie);
                            $Attach[] = $insdata;
                        }
                        reset($chaijie);
                        $insdata['lid'] = current($chaijie);//恢复第一条
                        unset($chaijie);
                    }
                }
                if (!isset($insdata['lid']) || empty($insdata['lid']['id'])) continue;
                if (!isset($insdata['lid']['subtable'])) $insdata['lid']['subtable'] = 0;
                $lidata = $insdata['lid'];
                $insdata['lid'] = $lidata['id'];
                $fill_rank = false;
                if(isset($lidata['rank']) && !isset($insdata['rank']))
                   {
                       $insdata['rank'] = $lidata['rank'];
                       $fill_rank = true;
                   }
                $dr = db_insert($labellogTB . $lidata['subtable'], $insdata);
                if ($dr != false) {
                    $lidstr = &$insdata['lid'];
                    if (!isset($inserted[$lidstr]))
                        $inserted[$lidstr] = 1;
                    else
                        $inserted[$lidstr]++;
                    $insdata['lid'] = $lidata;
                    if($fill_rank) unset($fill_rank,$insdata['rank']);
                    $ret[$dr] = &$insdata;
                    unset($insdata, $lidstr);//脱钩
                }
            }
        }
        if (!empty($Attach)) {//补加新增的标签记录:
            foreach ($Attach as &$curp2) {
                $lidata = $curp2['lid'];
                $curp2['lid'] = $lidata['id'];
                $fill_rank = false;
                if(isset($lidata['rank']) && !isset($curp2['rank']))
                   {
                       $curp2['rank'] = $lidata['rank'];
                       $fill_rank = true;
                   }
                $dr = db_insert($labellogTB . $lidata['subtable'], $curp2);
                if ($dr != false) {
                    $lidstr = &$curp2['lid'];
                    if (!isset($inserted[$lidstr]))
                        $inserted[$lidstr] = 1;
                    else
                        $inserted[$lidstr]++;
                    $curp2['lid'] = $lidata;
                    if($fill_rank) unset($fill_rank,$curp2['rank']);
                    $ret[$dr] = &$curp2;
                    unset($lidstr);
                }
            }
        }
        //处理统计:
        if (!empty($inserted)) {
            if (asort($inserted)) {//按次数排序:
                $labelTB = $ghx_cache->get_share_var(VAR_LABEL_TB);
                $times = current($inserted);
                $counter = array();
                foreach ($inserted as $lid => &$ti) {
                    if ($ti == $times) {
                        $counter[] = intval($lid);
                    } else {
                        db_update($labelTB, array('id' => $counter), array('counter+' => $times));
                        $counter = array(intval($lid));
                        $times = $ti;
                    }
                }
                //最后一段:
                db_update($labelTB, array('id' => $counter), array('counter+' => $times));
            }
        }
    }
    return $ret;
}


/**
 * 删除标签记录(支持批量)
 * @param array &$labelogConds 标签记录删除的条件数组,必须包含3项以上(含)键值或者包含id,必须包含dist,lid,或者id(如果指定ID,则优先,[指定ID的规则:在条件中加入subtable键,值为array({id}...)]dist,distid可以在其后的两个参数中统一指定)其中:dist一般为标签对象的表名代号(可以为表名字符串),lid为标签ID数据(如果分表号不是0,此项可以是array({标签数据集,必须有{id}和{subtable}})),也可以为标签字符串(警告:标签字符串的类型被处理为$table,如果非此,在调用前手动把标签转换为标签ID)
 * @param int $dataID 标签目标的数据ID,一般为表的ID,如果$labelogs看缺省distid,则用此代替
 * @param mixed|string $table 标签目标,即被贴标签的表名,也可以为表代码
 * @return int 删除的标签记录数
 */
function DelLabelLog(array &$labelogConds, int $dataID = 0, $table = 'thread')
{
    global $ghx_cache;
    $labellogTB = $ghx_cache->get_share_var(VAR_LABELOG_TB);
    $fields = $ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $labellogTB . '0');
    $ret = array();
    $LabelCfg = null;
    $tableId = $table;
    if (!empty($labelogConds)) {
        $test1 = current($labelogConds);
        $optArr = null;
        $delforidx = isset($labelogConds['subtable']) and !empty($labelogConds['subtable']);
        if (!is_array($test1) || $delforidx || isset($labelogConds['lid'])) {
            $optArr = array(&$labelogConds);
        } else {
            $optArr = &$labelogConds;
        }
        $Attach = array();
        $deleted = $deleted2 = array();
        foreach ($optArr as &$curp) {
            $delforid = isset($curp['subtable']) and !empty($curp['subtable']) and is_array(current($curp['subtable']));
            if (!isset($curp['dist']) && !$delforid) $curp['dist'] = $tableId;
            if (isset($curp['dist'])) {
                if (!is_numeric($curp['dist'])) {
                    if ($LabelCfg === null) $LabelCfg = &GetPluginCfgNode('Label');
                    if (!empty($LabelCfg['distcode']) && isset($LabelCfg['distcode'][$table]['id'])) {
                        $curp['dist'] = intval($LabelCfg['distcode'][$curp['dist']]['id']);
                        if (is_string($tableId)) {
                            $tableId = $curp['dist'];
                        }
                    }
                }
            }
            if (!$delforid && $dataID != 0 && empty($curp['distid'])) {
                $curp['distid'] = $dataID;
            }
            $subtable_href = null;
            if (isset($curp['subtable']) and !empty($curp['subtable'])) {
                $subtable_href = $curp['subtable'];//保存,后面再使用
            }
            $delConds = array_intersect_key($curp, $fields);//过滤无效字段,包括subtable
            $labellogTBName = null;
            if ($subtable_href != null) {
                if (is_array($subtable_href)) {
                    while (next($subtable_href) !== false) {
                        $delConds['table'] = key($subtable_href);
                        if (is_numeric($delConds['table'])) {
                            $delConds['table'] = $labellogTB . $delConds['table'];
                        }
                        $delConds['id'] = current($subtable_href);
                        $Attach[] = $delConds;
                    }
                    unset($delConds['table']);
                    reset($subtable_href);
                    $labellogTBName = key($subtable_href);
                    if (is_numeric($labellogTBName)) {
                        $labellogTBName = $labellogTB . $labellogTBName;
                    }
                    $delConds['id'] = current($subtable_href);
                } else {
                    if (is_object($subtable_href)) {
                        if (property_exists($subtable_href, 'subtable')) {
                            $labellogTBName = $subtable_href->subtable;
                            if (is_numeric($labellogTBName)) {
                                $labellogTBName = $labellogTB . $labellogTBName;
                            }
                            if (property_exists($subtable_href, 'id')) {
                                $delConds['id'] = $subtable_href->id;
                            }
                        }
                    } else {
                        //只指定了数据表名(subtable):
                        if (!is_numeric($subtable_href)) {
                            $labellogTBName = $subtable_href;
                        } else {
                            $labellogTBName = $labellogTB . $subtable_href;
                        }
                    }
                }
            }
            unset($subtable_href);
            if (count($delConds) < 3 && $labellogTBName == null) continue;
            $lidata = null;
            if (!empty($delConds['lid'])) {
                if (!is_array($delConds['lid'])) {
                    $LabelId = null;
                    if (!is_numeric($delConds['lid'])) {
                        $LabelIds = StrToLabel($delConds['lid'], $table, false);
                        if (!empty($LabelIds)) {
                            $LabelId = current($LabelIds);
                        } else
                            continue;
                        if (count($LabelIds) > 1) {
                            while (next($LabelIds) !== false) {
                                $delConds['lid'] = current($LabelIds);
                                $Attach[] = $delConds;
                            }
                        }
                        $delConds['lid'] = $LabelId;
                    } else {//只有ID,没有分表号:
                        if (is_string($delConds['lid'])) $delConds['lid'] = intval($delConds['lid']);
                        $subtbId = GetSubtableId($LabelCfg['SubLabellog'], $delConds['lid'], false);
                        $delConds['lid'] = array('id' => intval($delConds['lid']), 'subtable' => $subtbId);
                    }
                } else {
                    if (is_array(current($delConds['lid']))) {//多条合并成一组,需要拆解:
                        $chaijie = &$delConds['lid'];
                        unset($delConds['lid']);//解除联系,必须!
                        while (next($chaijie) !== false) {
                            $delConds['lid'] = current($chaijie);
                            $Attach[] = $delConds;
                        }
                        reset($chaijie);
                        $delConds['lid'] = current($chaijie);//恢复第一条
                        unset($chaijie);
                    }
                }
                if ($labellogTBName == null){
                    if (!isset($delConds['lid']) || empty($delConds['lid']['id'])) continue;
                    if (!isset($delConds['lid']['subtable'])) $delConds['lid']['subtable'] = 0;
                    $labellogTBName = $labellogTB . $delConds['lid']['subtable'];
                }
                if (isset($delConds['lid'])) {
                    $lidata = $delConds['lid'];
                    $delConds['lid'] = intval($lidata['id']);
                }
            } else {
                //lid没有指定的:
                if (!empty($labellogTBName) && !empty($delConds['id']) && empty($delConds['lid'])) {
                    $flidr = db_find($labellogTBName, array('id' => &$delConds['id']), array(), 1, count($delConds['id']), '', array('lid'));
                    if (!empty($flidr)) {
                        foreach ($flidr as &$plid) {
                            $clid = $plid['lid'];
                            if (!isset($deleted2[$clid])) $deleted2[$clid] = 0;
                            $deleted2[$clid]++;
                        }
                    }
                }
            }
            //执行删除:
            if (!empty($labellogTBName)) {
                $dr = db_delete($labellogTBName, $delConds);
                if ($dr > 0) {
                    if (isset($delConds['lid']) && !empty($delConds['lid'])) {
                        $lidstr = &$delConds['lid'];
                        if (!isset($deleted[$lidstr]))
                            $deleted[$lidstr] = 1;
                        else
                            $deleted[$lidstr]++;
                    }
                    if (!empty($deleted2)) {
                        foreach ($deleted2 as $clid => $cnts) {
                            if (!isset($deleted[$clid])) $deleted[$clid] = 0;
                            $deleted[$clid] += $cnts;
                        }
                    }
                    if($lidata !== null)
                        $delConds['lid']=$lidata;
                    $ret[] = &$delConds;
                    unset($delConds, $lidstr);//脱钩
                }
            }
            $deleted2 = array();//清空
        }
        if (!empty($Attach)) {//补加新增的标签记录:
            foreach ($Attach as &$curp2) {
                $lidata = $labellogTBName = null;
                if (isset($curp2['table'])) {
                    $labellogTBName = $curp2['table'];
                    unset($curp2['table']);
                    //lid没有指定的:
                    if (!empty($labellogTBName) && !empty($curp2['id']) && empty($curp2['lid'])) {
                        $flidr = db_find($labellogTBName, array('id' => &$curp2['id']), array(), 1, count($delConds['id']), '', array('lid'));
                        if (!empty($flidr)) {
                            foreach ($flidr as &$plid) {
                                $clid = $plid['lid'];
                                if (!isset($deleted2[$clid])) $deleted2[$clid] = 0;
                                $deleted2[$clid]++;
                            }
                        }
                    }
                } else {
                    $lidata = $curp2['lid'];
                    $curp2['lid'] = $lidata['id'];
                    $labellogTBName = $labellogTB . $lidata['subtable'];
                }
                if (!empty($labellogTBName)) {
                    $dr = db_delete($labellogTBName, $curp2);
                    if ($dr > 0) {
                        if (isset($curp2['lid']) && !empty($curp2['lid'])) {
                            $lidstr = &$curp2['lid'];
                            if (!isset($deleted[$lidstr]))
                                $deleted[$lidstr] = 1;
                            else
                                $deleted[$lidstr]++;
                        }
                        if (!empty($deleted2)) {
                            foreach ($deleted2 as $clid => $cnts) {
                                if (!isset($deleted[$clid])) $deleted[$clid] = 0;
                                $deleted[$clid] += $cnts;
                            }
                        }
                        $deleted2 = array();//清空
                        if($lidata !== null)
                            $curp2['lid']=$lidata;
                        $ret[] = &$curp2;
                        unset($lidstr);
                    }
                }
            }
        }
        unset($deleted2);
        //处理统计:
        if (!empty($deleted)) {
            if (asort($deleted)) {//按次数排序:
                $labelTB = $ghx_cache->get_share_var(VAR_LABEL_TB);
                $times = current($deleted);
                $counter = array();
                foreach ($deleted as $lid => &$ti) {
                    if ($ti == $times) {
                        $counter[] = intval($lid);
                    } else {
                        db_update($labelTB, array('id' => $counter), array('counter-' => $times));
                        $counter = array(intval($lid));
                        $times = $ti;
                    }
                }
                //最后一段:
                db_update($labelTB, array('id' => $counter), array('counter-' => $times));
            }
        }
    }
    return $ret;
}

/**
 * 清除指定文章关联的标签记录(注:是全部,不限于但包含文章关键词的标签记录,可能还有其它隐式的标签)
 * @param int $dataID 目标表的ID
 * @param string $table 目标表的名称或者代码,默认为thread
 * @return bool|int 清除的标签记录数量
 */
function ClearLabelLogFor(int $dataID, $table = 'thread'){
    global $ghx_cache;
    $ret = false;
    if(!empty($table) && $dataID != 0){
        if(!is_numeric($table)){
            $LabelCfg = &GetPluginCfgNode('Label');
            if (!empty($LabelCfg['distcode']) && isset($LabelCfg['distcode'][$table]['id'])) {
                $table = intval($LabelCfg['distcode'][$table]['id']);
            }
            else
                $table = 0;
        }
        else
            $table = intval($table);
        if($table > 0){
            global $db;
            $labellogTB = $ghx_cache->get_share_var(VAR_LABELOG_TB);
            $logtables = $ghx_cache->get_share_var(VAR_LIST_DB_TABLES,'#^'.preg_quote($db->tablepre,'#').'('.preg_quote($labellogTB,'#').'\d+)$#i');
            if(!empty($logtables)){
                //step 1:构造语句:
                $sqls = array();
                foreach ($logtables as $tb){
                    $sqls[$tb] = array('dist'=>$table,'distid'=>$dataID);
                    //$dataID为-1表示全部删除:
                    if($dataID == -1) unset($sqls[$tb]['distid']);
                }
                //step2:取出所有标签记录:
                $drows = array();
                foreach ($sqls as $tb => &$cond){
                    $dr = db_find($tb,$cond,array(),1,PHP_INT_MAX>>2,'',array('id','lid','distid','sign'));
                    if(!empty($dr)){
                        //foreach[&]效率更高
                        foreach($dr as &$drp) $drows[] = &$drp;
                    }
                    else
                    {
                        $cond = null;//没有记录的表下次跳过
                    }
                    unset($dr);
                }
                //step3:清除记录:
                $ret = 0;
                foreach ($sqls as $tb => &$cond){
                    if(!empty($cond)) $ret += intval(db_delete($tb,$cond));
                }
                //step4:执行其它动作:

                //step5:修正label表:
                if($ret > 0 && !empty($drows)){
                    $lids = array();
                    foreach($drows as &$dr1){
                        if(!isset($lids[$dr1['lid']])) $lids[$dr1['lid']] = 0;
                        $lids[$dr1['lid']]++;
                    }
                    if(asort($lids)) {
                        $labelTB = $ghx_cache->get_share_var(VAR_LABEL_TB);
                        $chgret = 0;
                        $delids = array();
                        $cur_t = current($lids);
                        foreach ($lids as $lid => $times) {
                            if($times == $cur_t) $delids[] = $lid;
                            else{
                                $chgret = db_update($labelTB,array('id'=>$delids),array('counter-'=>$cur_t));
                                $delids = array();
                                $cur_t = $times;
                            }
                        }
                        //最后一个$times:
                        $chgret = db_update($labelTB,array('id'=>$delids),array('counter-'=>$cur_t));
                    }
                }
            }
        }
    }
    return $ret;
}

/**
 * 修改标签记录(支持批量)
 * @param array &$labelogSearch 标签记录更新的条件数组,必须包含3项以上(含)键值或者包含id,必须包含dist,lid,或者指定ID[需要同时指定分表号,以array('subtable'=>array({分表号}=>array({id,...})))](dist,distid可以在其后的两个参数中统一指定)其中:dist一般为标签对象的表名代号(可以为表名字符串),lid为标签ID数据(如果分表号不是0,此项可以是array({标签数据集,必须有{id}和{subtable}})),也可以为标签字符串(警告:标签字符串的类型被处理为$table,如果非此,在调用前手动把标签转换为标签ID)
 * @param int $dataID 标签目标的数据ID,一般为表的ID,如果$labelogs看缺省distid,则用此代替
 * @param mixed|string $table 标签目标,即被贴标签的表名,也可以为表代码
 * @return int 更新的标签记录数
 */
function UpdateLabelLog(array &$labelogSearch, array &$updates_set, int $dataID = 0, $table = 'thread')
{
    if (empty($labelogSearch) || empty($updates_set)) return false;
    global $ghx_cache;
    $labellogTB = $ghx_cache->get_share_var(VAR_LABELOG_TB);
    $fields = $ghx_cache->get_share_var(VAR_TABLE_COLUMNS, $labellogTB . '0');
    $ret = 0;
    $LabelCfg = null;
    $tableId = $table;
    if (!empty($labelogSearch)) {
        $test1 = current($labelogSearch);
        $optArr = null;
        $updforidx = (isset($labelogSearch['subtable']) and !empty($labelogSearch['subtable']));
        if (!is_array($test1) || $updforidx || isset($labelogSearch['lid'])) {
            $optArr = array(&$labelogSearch);
        } else {
            $optArr = &$labelogSearch;
        }

        //处理$updates_set:
        $updates_set2 = &ArrayMatrixConversion($updates_set,true);
        if($updates_set2 !== false){
            $updates_set = &$updates_set2;
        }
        unset($updates_set2);

        $Attach = array();
        $upd_set_count = 1;
        if(is_array(current($updates_set)))
            $upd_set_count = count($updates_set);
        $upd_set_keys = array_keys($updates_set);
        $CurUpdSet = null;

        $optTables = array();
        $searchConds = array();
        $updates = array();

        foreach ($optArr as &$curp) {
            //与$updates_set同步推进
            if ($CurUpdSet === null)
                $cur_upd_set_key = current($upd_set_keys);
            elseif ($upd_set_count > 1) {
                $cur_upd_set_key = next($upd_set_keys);
                if ($cur_upd_set_key === false)
                    break;
            }
            if ($upd_set_count > 1 || $CurUpdSet === null) {
                $CurUpdSet = array_intersect_key($updates_set[$cur_upd_set_key], $fields);//过滤无效字段
                if (isset($CurUpdSet['id'])) unset($CurUpdSet['id']);//不允许修改ID
                if (empty($CurUpdSet)) {
                    continue;//$curp已经无意义
                }
            }
            $updforid = (isset($curp['subtable']) and !empty($curp['subtable']) and is_array(current($curp['subtable'])));
            if (!isset($curp['dist']) && !$updforid) $curp['dist'] = $tableId;
            if (isset($curp['dist'])) {
                if (!is_numeric($curp['dist'])) {
                    if ($LabelCfg === null) $LabelCfg = &GetPluginCfgNode('Label');
                    if (!empty($LabelCfg['distcode']) && isset($LabelCfg['distcode'][$table]['id'])) {
                        $curp['dist'] = intval($LabelCfg['distcode'][$curp['dist']]['id']);
                        if (is_string($tableId)) {
                            $tableId = $curp['dist'];
                        }
                    }
                }
                if ($dataID != 0 && empty($curp['distid'])) {
                    $curp['distid'] = $dataID;
                }
            }
            $subtable_href = null;
            if (isset($curp['subtable']) and !empty($curp['subtable'])) {
                $subtable_href = &$curp['subtable'];//保存,后面再使用
            }

            $updConds = array_intersect_key($curp, $fields);//过滤无效字段
            if(empty($updConds)) continue;

            $labellogTBName = null;
            $Attach = array();
            $Lid_add = 0;
            $Id_add = 0;

            if ($subtable_href != null) {
                if (is_array($subtable_href)) {
                    while (next($subtable_href) !== false) {
                        $labellogTBName = key($subtable_href);
                        if (is_numeric($labellogTBName)) {
                            $labellogTBName = $labellogTB . $labellogTBName;
                        }
                        $updConds['id'] = current($subtable_href);
                        //直接添加,而不缓存到$Attach里:
                        $optTables[] = $labellogTBName;//复制更新设置
                        $searchConds[] = $updConds;//不再考虑lid
                        $updates[] = $CurUpdSet;
                        $Id_add++;
                    }
                    reset($subtable_href);
                    unset($updConds['table']);
                    $updConds['id'] = current($subtable_href);
                    $labellogTBName = key($subtable_href);
                    if (is_numeric($labellogTBName)) {
                        $labellogTBName = $labellogTB . $labellogTBName;
                    }

                } else {
                    if (is_object($subtable_href)) {
                        if (property_exists($subtable_href, 'subtable')) {
                            $labellogTBName = $subtable_href->subtable;
                            if (is_numeric($labellogTBName)) {
                                $labellogTBName = $labellogTB . $labellogTBName;
                            }
                            if (property_exists($subtable_href, 'id')) {
                                $updConds['id'] = $subtable_href->id;
                            }
                        }
                    } else {
                        //只指定了数据表名(subtable):
                        if (!is_numeric($subtable_href)) {
                            $labellogTBName = $subtable_href;
                        } else {
                            $labellogTBName = $labellogTB . $subtable_href;
                        }
                    }
                }
            }
            unset($subtable_href);
            $lidata = null;
            if (!empty($updConds['lid'])) {
                if (!is_array($updConds['lid'])) {
                    $LabelId = null;
                    if (!is_numeric($updConds['lid'])) {
                        $LabelIds = StrToLabel($updConds['lid'], $table, false);
                        if (!empty($LabelIds)) {
                            $LabelId = current($LabelIds);
                        } else
                            continue;
                        if (count($LabelIds) > 1 && ($labellogTBName != null || isset($updConds['lid']['id']))) {
                            while (next($LabelIds) !== false) {
                                $updConds['lid'] = current($LabelIds);
                                if ($labellogTBName == null && !isset($updConds['lid']['subtable']))
                                    $updConds['lid']['subtable'] = 0;
                                $Attach[] = $updConds;//复制更新设置1
                                $Lid_add++;
                            }
                        }
                        $updConds['lid'] = $LabelId;
                    } else {//只有ID,没有分表号:
                        if (is_string($updConds['lid'])) $updConds['lid'] = intval($updConds['lid']);
                        $subtbId = GetSubtableId($LabelCfg['SubLabellog'], $updConds['lid'], false);
                        $updConds['lid'] = array('id' => $updConds['lid'], 'subtable' => $subtbId);
                    }
                } else {
                    if (is_array(current($updConds['lid']))) {//多条合并成一组,需要拆解:
                        $chaijie = &$updConds['lid'];
                        unset($updConds['lid']);//解除联系,必须!
                        if (count($chaijie) > 1 && ($labellogTBName != null || isset($updConds['lid']['id']))) {
                            while (next($chaijie) !== false) {
                                $updConds['lid'] = current($chaijie);
                                if ($labellogTBName == null && !isset($updConds['lid']['subtable']))
                                    $updConds['lid']['subtable'] = 0;
                                $Attach[] = $updConds;//复制更新设置2
                                $Lid_add++;
                            }
                            reset($chaijie);
                        }
                        $updConds['lid'] = current($chaijie);//恢复第一条
                        unset($chaijie);
                    }
                }
                if ($labellogTBName == null) {
                    if (!isset($updConds['lid']) || empty($updConds['lid']['id'])) {
                        array_splice($Attach, $Lid_add * -1);//删除刚才增加不合格的
                        continue;
                    }
                    if (!isset($updConds['lid']['subtable'])) $updConds['lid']['subtable'] = 0;
                    $labellogTBName = $labellogTB . $updConds['lid']['subtable'];
                }
                if (isset($updConds['lid'])) {
                    $lidata = $updConds['lid'];
                    $updConds['lid'] = $lidata['id'];
                }
            }

            //增加到队列:
            if (!empty($labellogTBName)) {
                $optTables[] = $labellogTBName;
                $searchConds[] = $updConds;
                $updates[] = $CurUpdSet;
                if (!empty($Attach)) {
                    foreach ($Attach as &$aitem) {
                        $optTables[] = $labellogTBName;
                        $searchConds[] = &$aitem;
                        $updates[] = $CurUpdSet;
                    }
                }
            }
        }

        $updtimes = count($updates);
        if (count($optTables) != count($searchConds) || count($searchConds) != $updtimes)
            return false;
        for ($i = 0; $i < $updtimes; $i++) {
            $lidata = null;
            if (!empty($updates[$i]) && !empty($searchConds[$i]) && !empty($optTables[$i])) {
                if (isset($searchConds[$i]['lid']) && is_array($searchConds[$i]['lid'])) {
                    $lidata = $searchConds[$i]['lid'];
                    if (empty($lidata['id'])) continue;
                    $searchConds[$i]['lid'] = $lidata['id'];
                }
                $dr = db_update($optTables[$i], $searchConds[$i], $updates[$i]);
                if ($dr > 0) {
                    if ($lidata != null) $searchConds['lid'] = $lidata;
                    $ret += (int)$dr;
                }
            }
        }
    }
    return $ret;
}


/**
 * 获取标签表的某项数据,会预处理各输入项转换为标签ID(不会新增标签,如要新增使用StrToLabel())
 * @param mixed &$labels 字符串/ID/标签表查询结果,以及此类之数组
 * @param string|array $fieldlist 要获取的字段名
 * @param mixed $getRank 是否设置关键词权重,默认为-1检测配置,false:否,true:是,-1:检测(一行关键词,从左到右,权重值从大到小),不存在则为[2,1]
 * @return array|bool 错误返回false,数据格式:count($fieldlist)=1:array({id}=>{$field});多个字段时:array({id}=>array({$fields}))
 */
function &GetLabelData(&$labels, $fieldlist, $getRank = -1)
{
    $ret = false;
    if (!empty($labels) && !empty($fieldlist)) {
        global $ghx_cache;
        $labelN = $ghx_cache->get_share_var(VAR_LABEL_TB);
        $fields = $ghx_cache->get_share_var(VAR_TABLE_COLUMNS,$labelN);
        if (is_string($fieldlist)) {
            $fieldlist = preg_split('#[,;\|\t\n \\\\\/`]+#s', $fieldlist, -1, PREG_SPLIT_NO_EMPTY);
        }
        array_unshift($fieldlist, 'id');
        $fieldlist = array_unique($fieldlist);
        foreach ($fieldlist as $k => $v) {
            if (!isset($fields[$v])) unset($fieldlist[$k]);//过滤
        }
        if (!empty($fieldlist)) {
            if (!is_array($labels)) $labels = array($labels);
            $selablesWord = array();//字符组
            $selablesId = array();//ID组
            $selqueue = array();//全部,最后排列用

            foreach ($labels as &$label_node) {
                if (!is_array($label_node) && !is_numeric($label_node)) {
                    $tmpkws = preg_split('#[,;\|\t\n\\\/]+|\s{2,}#s', $label_node, -1, PREG_SPLIT_NO_EMPTY);
                    foreach($tmpkws as &$_kw_trim){
						$_kw_trim = trim($_kw_trim);
					}
					$selablesWord = $tmpkws + $selablesWord;
                    $selqueue = $tmpkws + $selqueue;
                } else {
                    if (is_array($label_node)) {
                        if (isset($label_node['id']) && !empty($label_node['id'])) {
                            if (is_numeric($label_node['id'])) {
                                $selablesId[] = $label_node['id'];
                                $selqueue[] = $label_node['id'];
                            } elseif (is_array($label_node['id'])) {
                                $selablesId = $label_node['id'] + $selablesId;
                                if (is_array(current($label_node['id']))) {
                                    foreach ($label_node['id'] as &$subItemv) {
                                        if (is_array($subItemv)) {
                                            if (isset($subItemv['id']))
                                                $selqueue[] = $subItemv['id'];
                                            elseif (isset($subItemv['name']))
                                                $selqueue[] = $subItemv['name'];
                                        }
                                    }
                                } else
                                    $selqueue = $label_node['id'] + $selqueue;
                            }
                        } else {
                            $selqueue = $label_node + $selqueue;
                            if (!is_numeric(current($label_node)))
                                $selablesWord = $label_node + $selablesWord;
                            else {
                                $selablesId = $label_node + $selablesId;
                            }
                        }
                    } elseif (is_numeric($label_node)) {
                        $selablesId[] = $label_node;
                        $selqueue[] = $label_node;
                    }
                }
            }
            $selablesWord = array_unique($selablesWord);
            $selablesId = array_unique($selablesId);
            $selqueue = array_unique($selqueue);
            $selRows = array();
            if (!empty($selablesWord)) {
                $myfields = $fieldlist;
                $addNameF = false;
                if (!in_array('name', $fieldlist)) {
                    $myfields[] = 'name';
                    $addNameF = true;
                }
                $selRows = db_find($labelN, array('name' => $selablesWord), array(), 1, count($selablesWord), 'id', $myfields);
                if (empty($selRows)) $selRows = array();
                else {
                    //把$selqueue中的字符串转化为ID
                    foreach ($selRows as $rid => &$row) {
                        if (isset($selqueue[$row['name']])) {
                            $selqueue[$row['name']] = intval($row['id']);
                        } else
                            $selqueue[] = intval($row['id']);//增加入
                        if ($addNameF) unset($row['name']);
                    }
                    $selqueue = array_unique($selqueue);
                }
                unset($addNameF, $myfields);
            }
            //取粜只有ID而没有subtable的记录
            $getSubtable = array();
            $unsetKey = array();
            foreach ($selablesId as $jk => &$mix) {
                if (is_numeric($mix)) {
                    $getSubtable[] = &$mix;
                    $unsetKey[] = $jk;
                } elseif (is_array($mix)) {
                    if (isset($mix['id'])) {
                        foreach ($fieldlist as $f) {
                            if (!isset($mix[$f])) {
                                $getSubtable[] = $mix['id'];
                                $unsetKey[] = $jk;
                                break;
                            }
                        }
                    } else {
                        $unsetKey[] = $jk;//无效,删除
                    }
                }
            }
            if (!empty($unsetKey)) {
                for ($i = count($unsetKey) - 1; $i >= 0; $i--) {
                    unset($selablesId[$unsetKey[$i]]);
                }
            }
            unset($unsetKey);
            $SeeRows = db_find($labelN, array('id' => $getSubtable), array(), 1, count($getSubtable), 'id', $fieldlist);
            if (empty($SeeRows))
                $SeeRows = $selRows + $selablesId;//合并
            else
                $SeeRows = $SeeRows + $selRows + $selablesId;//合并
            if (!empty($SeeRows)) {
                //根据$selqueue排列$SeeRows
                $SeeRows2 = array();
                foreach ($selqueue as $pid) {
                    if (isset($SeeRows[$pid])) {
                        $SeeRows2[$pid] = &$SeeRows[$pid];
                        unset($SeeRows[$pid]);
                    }
                }
                foreach ($SeeRows as $Kid => &$KVP) {//漏网之鱼,附加
                    $SeeRows2[$Kid] = &$KVP;
                }
                unset($SeeRows);
                $SeeRows = &$SeeRows2;//替换之
                unset($SeeRows2);
                $ret = array();
                $fcnt = count(current($SeeRows));

                $ArrangementWeightsCfg = null;
                $awLen = 0;
                $lastAW = 0;
                //rank前置处理
                if ($getRank == -1) {
                    $ArrangementWeightsCfg = &GetPluginCfgNode('ArticleKeyWord/ArrangementWeights');
                    if ($ArrangementWeightsCfg != null && !empty($ArrangementWeightsCfg))
                        $getRank = true;
                    else
                        $getRank = false;
                }
                if ($getRank == true) {
                    $fcnt++;
                    if ($ArrangementWeightsCfg === null)
                        $ArrangementWeightsCfg = &GetPluginCfgNode('ArticleKeyWord/ArrangementWeights');//引用更快
                    if (empty($ArrangementWeightsCfg)) {
                        $ArrangementWeightsCfg = array(2, 1);
                    }
                    $awLen = count($ArrangementWeightsCfg);
                    $lastAW = $ArrangementWeightsCfg[$awLen - 1];
                }
                $cur_pos = 0;
                foreach ($SeeRows as $k => &$v) {
                    if ($fcnt == 2) $ret[$k] = $v[$fieldlist[1]];
                    else {
                        if ($getRank == true && !isset($v['rank'])) {//rank处理
                            if ($cur_pos < $awLen)
                                $v['rank'] = $ArrangementWeightsCfg[$cur_pos];
                            else
                                $v['rank'] = $lastAW;
                        }
                        $ret[$k] = &$v;
                    }
                    $cur_pos++;
                }
            }
        }
    }
    return $ret;
}

/**
 * 删除指定标签,会删除对应的标签记录
 * @param mixed $labels 要删除的标签列表,可以是字符串,可以是ID数组,也可以是Label表的查询结果
 * @return array|bool|int 如果成功,返回已经删除的标签ID数组
 */
function DelGhxLabel($labels)
{
    $ret = 0;
    if (!empty($labels)) {
        $SeeRows = GetLabelData($labels, 'subtable');
        $delablesId = $delSubtables = array();
        //分类汇总
        if (!empty($SeeRows)) {
            foreach ($SeeRows as $curId => $subv) {
                $delablesId[] = $curId;
                if (isset($delSubtables[$subv]))
                    $delSubtables[$subv][] = intval($curId);
                else
                    $delSubtables[$subv] = array(intval($curId));
            }

        }
        //先删除记录表:
        foreach ($delSubtables as $subt => &$lids) {
            db_delete(GhxCache::getInstance()->get_share_var(VAR_LABELOG_TB) . $subt, array('id' => $lids));
        }
        //再删除标签表:
        $ret = db_delete(GhxCache::getInstance()->get_share_var(VAR_LABEL_TB), array('id' => $delablesId));
        if ($ret > 0) {
            $ret = &$delablesId;
        }
    }
    return $ret;
}

/**
 * 设置标签的cate属性
 * @param mixed &$labels 要设置Cate的标签列表,可以是字符串,可以是ID数组,也可以是Label表的查询结果
 * @param int $Cate 设置为目标Cate的值
 * @param int|string $optType 操作类型:添加:1(|,+,or,add);删除:2(-,del,clr);仅保留:3(&,reserve,rev);异或:4(^,xor);取反:5(~,neg)
 * @param bool $clearEmptyCateLabel 是否删除操作后cate为0的标签
 * @return bool
 */
function SetLabelCate(&$labels, int $Cate, $optType, $clearEmptyCateLabel = false)
{
    $ret = false;
    if (!empty($labels)) {
        $DataRows = GetLabelData($labels, 'cate');
        if (!empty($DataRows)) {
            if ($clearEmptyCateLabel) {
                $dels = array();
                $other = array();
                foreach ($DataRows as $k => $v) {
                    switch ($optType) {
                        case 1:
                        case '|':
                        case '+':
                        case "or":
                        case "add":
                            $v |= $Cate;
                            $optType = 1;
                            break;
                        case 2:
                        case '-':
                        case "del":
                        case "clr":
                            $v &= (~$Cate);
                            $optType = 2;
                            break;
                        case 3:
                        case '&':
                        case "reserve":
                        case "rev":
                            $v &= $Cate;
                            $optType = 3;
                            break;
                        case 4:
                        case '^':
                        case "xor":
                            $v ^= $Cate;
                            $optType = 4;
                            break;
                        case 5:
                        case '~':
                        case "neg":
                            $v = ~$v;
                            $optType = 5;
                            break;
                        default:
                            break;
                    }
                    if ($v == 0)
                        $dels[] = $k;
                    elseif ($DataRows[$k] != $v)
                        $other[] = $k;
                }
                $DataRows = &$other;
                if (!empty($dels)) {
                    DelGhxLabel($dels);
                }
            } else {
                $DataRows = array_keys($DataRows);
            }
            if (!empty($DataRows)) {
                $sql = '';
                $ids = implode(',', $DataRows);
                switch ($optType) {
                    case 1:
                        $sql = '`cate`=`cate`|' . $Cate;
                        break;
                    case 2:
                        $sql = '`cate`=(~`cate`)&' . $Cate;
                        break;
                    case 3:
                        $sql = '`cate`=`cate`&' . $Cate;
                        break;
                    case 4:
                        $sql = '`cate`=`cate`^' . $Cate;
                        break;
                    case 5:
                        $sql = '`cate`= ~`cate`';
                        break;
                    default:
                        break;
                }
                if (!empty($sql)) {
                    global $tablepre;
                    $sql = 'UPDATE ' . $tablepre . GhxCache::getInstance()->get_share_var(VAR_LABEL_TB) . ' SET ' . $sql . ' WHERE `id` IN(' . $ids . ')';
                    $ret = db_exec($sql);
                }
            }
        }
    }
    return $ret;
}

/**
 * 根据条件获取标签记录(结果不归类,即可能出现多条标签对应一篇文章)
 * @param string|int|array $labels 标签列表,可以是字符串,可以是ID数组,也可以是Label表的查询结果
 * @param array &$conditions 查询条件
 * @param array|null &$orderby 排序方式
 * @param string|array|null $fields 要查询的字段,空为全部,默认为base,如果为base,则为:array('lid','dist','distid','distclass','rank','sign')
 * @return array|bool array({Labelog表记录}...),结果不归类
 */
function &ListLabelog($labels, array &$conditions, array &$orderby = null, $fields = 'base')
{
    $dbresult = $falseval = false;
    if (!empty($labels)) {
        $labelIds = &GetLabelData($labels, 'counter,subtable');
        if (!empty($labelIds)) {
            $labelSubs = array();
            foreach ($labelIds as $k => &$v) {
                if ($v['counter'] < 1) unset($labelIds[$k]);
                else { //根据分表分类:
                    $subtable = &$v['subtable'];
                    if (!isset($labelSubs[$subtable])) $labelSubs[$subtable] = array();
                    $labelSubs[$subtable][] = $k;
                    unset($subtable);
                }
            }
            if (!empty($labelSubs)) {
                //先处理参数fid
                if (isset($conditions['fid'])) {
                    if (!empty($conditions['fid'])) {
                        if (is_string($conditions['fid'])) {
                            $conditions['fid'] = preg_split('#[,;\|\t\n \\\\\/]+#s', $conditions['fid'], -1, PREG_SPLIT_NO_EMPTY);
                        }
                        $conditions['distclass'] = $conditions['fid'];
                    }
                }
                if(empty($conditions['fid'])) unset($conditions['fid']);
                //处理dist
                if(isset($conditions['dist']) && !is_numeric($conditions['dist'])){
                    if(ConverseDistCode($conditions['dist'])== false)
                        return $falseval;
                }
                elseif(isset($conditions['table']) && !is_numeric($conditions['table'])){
                    if(ConverseDistCode($conditions['table'])== false)
                        return $falseval;
                    $conditions['dist'] = &$conditions['table'];
                    unset($conditions['table']);
                }
                $tbname = GhxCache::getInstance()->get_share_var(VAR_LABELOG_TB);
                $dbresult = array();
                if (!empty($fields) && is_string($fields)) {
                    if($fields == 'base'){
                        $fields = array('lid','dist','distid','distclass','rank','sign');
                    }
                    else
                        $fields = preg_split('#[,;\|\t\n \\\\\/`]+#s', $fields, -1, PREG_SPLIT_NO_EMPTY);
                }
                foreach ($labelSubs as $subno => &$s_lids) {
                    $conditions['lid'] = &$s_lids;
                    $dr = db_find($tbname . $subno, $conditions, $orderby, 1, PHP_INT_MAX>>2, '', $fields);//不排序
                    if (!empty($dr)) $dbresult = array_merge($dbresult, $dr);
                    unset($conditions['lid']);//解除引用
                }
                if(!empty($dbresult)){//字段值类型规范化:
                    $intcols = array('dist','distclass','distid','id','lid','rank','vint','sign');
                    foreach ($dbresult as &$dbr1){
                        foreach($dbr1 as $rfk => &$rfv){
                            if($rfv !== NULL && in_array(strtolower($rfk),$intcols))
                                $rfv = intval($rfv);
                            elseif($rfk=='vdouble'){
                                $rfv = doubleval($rfv);
                            }
                        }
                    }
                }
                return $dbresult;
            }
        }
    }
    return $dbresult;
}

/**
 * 根据标签记录优选目标数据
 * @param array &$Labelogs 已经select到的标签记录数据,一般是ListLabelog()返回的结果
 * @param array &$LabelIds 参考LabelId列表,即只取这个范围内的标签记录,空为$Labelogs的全部
 * @param array|string $relatedfids $Labelogs中允许哪些distclass,不满足者跳过,null为$Labelogs的全部
 * @param array &$ExcludeDistRows 要排除的數據行,即labelog{n}表中的dist和distid,此數組必須包含dist/distid或者table/id.目的:排除當前文章自身,一般来说是ParseRelatedLinkExclude()的结果
 * @param integer $pick_count 选取目标数量,-1为检测配置(ArticleKeyWord/RelatedLinkViewCount),默认为10个
 * @param string|array $SortItem 排序项目,可以为函数名,也可能为字段名;默认为rank
 * @param integer $ItemType 排序项目的类型,1:用户函数,0:字段名(默认)
 * @param integer|float $increase 在记录排序数据(通常是rank)上增加的数值(默认为0),可以为小数,防止为0相加得0的不合理统计
 * @param float $NewestWeights 新文章的权重(按ID判断),-1為從配置中加载,配置不存在时设为0.25,建议从0.01~0.99之间,以0.5为适中值
 * @return array|bool 错误返回false,否则返回array(array('dist','distid','rank')...),注:只有dist和distid,rank!
 */
function &SelectiveLabelogs(array &$Labelogs, array &$LabelIds,$relatedfids=null,array &$ExcludeDistRows=null, $pick_count = -1, $SortItem = 'rank', int $ItemType = 0, $increase = 0, float $NewestWeights = -1)
{
    global $ghx_cache;
    $ret = false;
    if (!empty($Labelogs) && !empty($SortItem) && (is_string($SortItem) || is_array($SortItem)) && ($pick_count > 0 || $pick_count==-1)) {
        $isFun = -1;
        if ($ItemType == 1) {
            if (is_string($SortItem)) {
                if (function_exists($SortItem)) $isFun = 1;
            } elseif (is_array($SortItem) && count($SortItem) > 1) {
                if (method_exists($SortItem[0], $SortItem[1])) $isFun = 1;
            }
        } else{
            $isFun = 0;
            $test1 = current($Labelogs);
            if(!isset($test1[$SortItem]))
                return $ret;
        }

        $log1 = current($Labelogs);
        if (isset($log1['lid']) && isset($log1['dist']) && isset($log1['distid']) && ($isFun == 0 && isset($log1[$SortItem])) || $isFun > 0) {
            if (is_string($increase)) $increase = doubleval($increase);
            $SortLine1 = array();
            $filterLid = !empty($LabelIds);
            $ExcludeDist = !empty($ExcludeDistRows);
            if($ExcludeDist){//檢查排除條件
                $edrtest = current($ExcludeDistRows);
                if((!isset($edrtest['dist']) || !isset($edrtest['distid'])) && (!isset($edrtest['table']) || !isset($edrtest['id'])))
                    $ExcludeDist = false;
                unset($edrtest);
                if($ExcludeDist){//数字转化:
                    foreach($ExcludeDistRows as $edk => &$edv){
                        if(is_array($edv)){
                            foreach($edv as $edk1 => &$edv1){
                                if(in_array($edk1,array('distid','id','lid'),true)){
                                    $edv1 = intval($edv1);
                                }
                            }
                        }
                        else{
                            if(!is_numeric($edk)){
                                if(in_array($edk,array('distid','id','lid'),true))
                                    $edv = intval($edv);
                            }
                        }
                    }
                }
            }
            $LabelCfg = &GetPluginCfgNode('Label');
            if (!empty($LabelCfg) && isset($LabelCfg['distcode'])) {
                $DistCodeCfg = &$LabelCfg['distcode'];
                $DistCodes = array();
                foreach ($DistCodeCfg as $dc => &$dcv) {
                    $DistCodes[$dcv['id']] = $dc;
                }
            }
            $AKWCfg = null;
            if($NewestWeights == -1 || $pick_count == -1 || $filterLid)
                $AKWCfg = &GetPluginCfgNode('ArticleKeyWord');
            if($NewestWeights == -1){
                if(isset($AKWCfg['SearchMatchNewestWeighted'])){
                    $NewestWeights = $AKWCfg['SearchMatchNewestWeighted'];
                }
                else
                    $NewestWeights = 0.25;//默认值
            }
            if($pick_count == -1){//关联文章的数量:
                if(isset($AKWCfg['RelatedLinkViewCount'])){
                    $pick_count = intval($AKWCfg['RelatedLinkViewCount']);
                }
                else
                    $pick_count = 10;//默认值
            }
            $SMSerialWeighteds = array(1.66,1.33,1.33,1.0);//默认值
            if($filterLid){
                if(isset($AKWCfg['SearchMatchSerialWeighted']) && !empty($AKWCfg['SearchMatchSerialWeighted'])){
                    $SMSerialWeighteds = $AKWCfg['SearchMatchSerialWeighted'];
                }
                if(is_array(current($LabelIds)))
                {//格式:array({name}=>array({id},{name},{subtable},{cate}))
                    $tmpArr = array();
                    foreach($LabelIds as &$labelId){
                        if(isset($labelId['id']))
                            $tmpArr[] =  intval($labelId['id']);
                        elseif(isset($labelId['lid']))
                            $tmpArr[] =  intval($labelId['lid']);
                    }
                    unset($LabelIds);
                    $LabelIds = &$tmpArr;
                    unset($tmpArr);
                }
            }
            //把表名转换为代码:
            foreach($ExcludeDistRows as &$edrow){
                if(isset($edrow['dist']) && !is_numeric($edrow['dist']) && isset($DistCodeCfg[$edrow['dist']]['id']))
                    $edrow['dist'] = $DistCodeCfg[$edrow['dist']]['id'];
                if(isset($edrow['table']) && !is_numeric($edrow['table']) && isset($DistCodeCfg[$edrow['table']]['id']))
                    $edrow['table'] = $DistCodeCfg[$edrow['table']]['id'];
            }
            if(!empty($relatedfids)){
                if(is_string($relatedfids)){
                    $relatedfidsX = explode(',',$relatedfids);
                    unset($relatedfids);
                    $relatedfids=&$relatedfidsX;
                    unset($relatedfidsX);
                }
                if(is_string(current($relatedfids))) {
                    $_relatedfids = array();
                    foreach ($relatedfids as $relatedfid) {
                        if(is_numeric($relatedfid)){
                            $_v = intval($relatedfid);
                            if($_v > 0){
                                $_relatedfids[] = $_v;
                            }
                        }
                    }
                    unset($relatedfids);
                    $relatedfids = &$_relatedfids;
                    unset($_relatedfids);
                }
                $relatedfidsX = array_unique($relatedfids);
                unset($relatedfids);
                $relatedfids=&$relatedfidsX;
                unset($relatedfidsX);
            }
            foreach ($Labelogs as $k => &$v) {
                //筛选LabelId:
                if ($filterLid) {
                   if (!in_array($v['lid'], $LabelIds,true)) {
                      continue;//跳过不符合lid条件的
                   }
                }
                if(is_string($v['distclass'])) $v['distclass'] = intval($v['distclass']);
                if(!empty($relatedfids)){
                    if(!in_array($v['distclass'],$relatedfids,true))
                        continue;//跳过不符合distclass条件的
                }
                if($ExcludeDist){
                    $goback_continue = false;
                    foreach($ExcludeDistRows as &$edrow){
                        if(isset($edrow['mark']) && !empty($edrow['mark'])){
                            if($edrow['mark']!='-' && $edrow['mark']!='!' && $edrow['mark']!='+' && $edrow['mark']!='*')
                                continue;
                        }
                        if(isset($edrow['dist'])){//dist和distid组合,优先
                            if($edrow['dist']===$v['dist'] && $edrow['distid']===$v['distid']) {
                                $goback_continue = true;
                                break;//跳过要排除的dist
                            }
                        }
                        if(isset($edrow['table'])){//table和id组合
                            if($edrow['table']===$v['dist'] && $edrow['id']===$v['distid']) {
                                $goback_continue = true;
                                break;//跳过要排除的dist
                            }
                        }
                    }
                    if($goback_continue) continue;
                }
                $dist = &$v['dist'];
                $distid = &$v['distid'];
                if(!is_int($v[$SortItem])) $v[$SortItem] = floatval($v[$SortItem]);
                if($v[$SortItem] == 0) $v[$SortItem] = 0.1;//最小值
                if (!isset($DistCodes[$dist]))
                    continue;//跳过配置里没有的表
                $lrank = 0;
                $smsw_rate = GetSearchMatchSerialWeighted($LabelIds,$SMSerialWeighteds,$v['lid']);
                switch ($ItemType) {
                    case 1://用户函数:
                        $v['increase'] = $increase;
                        $v['SearchMatchSerialWeighted'] = $smsw_rate;
                        $lrank = call_user_func_array($SortItem, $v);
                        unset($v['increase'],$v['SearchMatchSerialWeighted']);//复原,其它地方可能要用这组数据
                        break;
                    default://指定字段
                        $lrank = $v[$SortItem]*$smsw_rate + $increase;
                        break;
                }
                $kw = strval($dist) . '|' . strval($distid);
                if (!isset($SortLine1[$kw])) $SortLine1[$kw] = $lrank;
                else $SortLine1[$kw] += $lrank;
                unset($dist, $distid, $kw);
            }
            //排序:
            if (arsort($SortLine1)) {
                //选取$pick_count个:
                $slc = count($SortLine1);
                $cand_inc = $pick_count * 0.2; //候选增量 Candidate increment
                if ($slc < $pick_count) {
                    $pick_count = $slc;
                    $cand_inc = 0;
                }
                $ret = array();
                $haved = 0;
                $cand_Line = array();
                $prev_rank = null;
                $cur_rank_sum = null;
                //VAR_APPR_MAXID
                foreach ($SortLine1 as $k => $rk) {
                    $p = strpos($k, '|', 1);
                    $dist = intval(substr($k, 0, $p));
                    $distid = intval(substr($k, $p + 1));
                    if ($haved >= $pick_count) {
                        if ($haved == $pick_count) {
                            $prev_rank = $rk;
                            $cur_rank_sum = 0;
                        }
                        $cand_Line_length = count($cand_Line);
                        if ($cand_Line_length < $cand_inc || $rk == $prev_rank) {
                            $cand_Line[] = array('dist' => $dist, 'distid' => $distid, 'rank' => $rk);
                            $cand_Line_length++;
                            if ($rk != $prev_rank) {
                                $prev_rank = $rk;
                                $cur_rank_sum = 0;
                            } else {
                                $cur_rank_sum++;
                                if ($cand_Line_length > $cand_inc * 2) {//计划外的数量过多
                                    if ($cand_Line_length - $cur_rank_sum > $cand_inc / 2.5) {
                                        array_splice($cand_Line, $cur_rank_sum * -1);//删除掉尾巴
                                        break;
                                    }
                                }
                            }
                        } else
                            break;
                    } else
                        $ret[] = array('dist' => $dist, 'distid' => $distid, 'rank' => $rk);
                    $haved++;
                }
                if (!empty($cand_Line)) {
                    $ret = array_merge($ret, $cand_Line);
                }
                unset($haved, $cand_Line, $prev_rank, $cur_rank_sum);
                $haved = doubleval(count($ret));
                if ($pick_count < $haved) {
                    //计算rank和lastid的平均值:
                    $RankSum = 0;
                    $RankMax = null;
                    foreach ($ret as &$rkv) {
                        if ($RankMax == null) $RankMax = $rkv['rank'];
                        $RankSum += $rkv['rank'];
                    }
                    $avgrank = $RankSum / $haved;
                    $RankMaxDiff = $RankMax - $avgrank;
                    $sortarr = $ret;//复制一份
                    $LatestSum = 0;
                    $LatestMin = null;
                    foreach ($sortarr as &$v) {
                        $dc_table = $DistCodes[$v['dist']];
                        if (!empty($dc_table)) {
                            $Latest = $ghx_cache->get_share_var(VAR_APPR_MAXID, $dc_table) - $v['distid'];
                            if ($Latest < 0) {
                                $Latest = $ghx_cache->get_share_var(VAR_APPR_MAXID, $dc_table, true) - $v['distid'];//更新最大值
                                if ($Latest < 0) $Latest = 0;
                            }
                            $v['Latest'] = $Latest;
                            $LatestSum += $Latest;
                            if ($LatestMin === null) $LatestMin = $Latest;
                            else {
                                if ($Latest < $LatestMin) $LatestMin = $Latest;
                            }
                        }
                    }
                    $avgLatest = $LatestSum / $haved;
                    $NewestMinDiff = $avgLatest - $LatestMin;
                    $NewestRate = $RankMaxDiff * $NewestWeights / $NewestMinDiff;
                    //微调rank
                    foreach ($sortarr as &$v) {
                        $v['rank'] += ($avgLatest - $v['Latest']) * $NewestRate;
                        unset($v['Latest']);
                    }
                    //排序
                    $ret = arrlist_multisort($sortarr, 'rank', FALSE);
                    //删除多余的
                    array_splice($ret, $pick_count);
                }
            }
        }
    }
    return $ret;
}

/**
 * 根据排序好的Labelog记录生成关联链接,以便保存到数据库
 * @param array $LabelogTops 已经排序的Labelog记录,一般是SelectiveLabelogs()的结果
 * @param int $LinkCount 关联链接的数量,默认为-1,即$LabelogTops数组的全部
 * @param array|string $ExcludeUrls 要排除的链接,字符串者,一行一条,首字符为-或者!,其余忽略,URL部分小于5个字符时,取其后的dist和distid;数组时具有同样的结构;可以是ParseRelatedLinkExclude()的结果
 * @param string|null $template 关联链接的模板,默认为插件配置项:Label/RelatedLinkTemplate/line (如果都不存在,则用:[{url}]<{table},{id}>{title})
 * @return array|null 根据关联链接的模板生成$LinkCount个字符串数组
 */
function &GenerateRelatedLinks(array &$LabelogTops, $LinkCount = -1, $ExcludeUrls = null, string $template = null)
{
    global $ghx_cache;
    $ret = null;
    $LabelCfg = null;
    if ($template == null) {
        $LabelCfg = &GetPluginCfgNode('Label');
        if ($LabelCfg != null && isset($LabelCfg['RelatedLinkTemplate']['line']))
            $template = $LabelCfg['RelatedLinkTemplate']['line'];
    }
    if ($template == null)
        $template = '[{url}]<{table},{id}>{title}';
    if (!empty($LabelogTops) && ($LinkCount > 0 || $LinkCount == -1)) {
        $data1 = current($LabelogTops);
        if (isset($data1['dist']) && isset($data1['distid']) && $LinkCount != 0) {
            if ($LabelCfg == null) $LabelCfg = &GetPluginCfgNode('Label');
            if (!empty($LabelCfg) && isset($LabelCfg['distcode'])) {
                $DistCodeCfg = &$LabelCfg['distcode'];
                $DistCodes = array();
                foreach ($DistCodeCfg as $dc => &$dcv) {
                    $DistCodes[$dcv['id']] = $dc;
                }
                if ($LinkCount == -1)
                    $LinkCount = count($LabelogTops);
                //准备排除列表:
                if (!empty($ExcludeUrls)) {
                    if (is_string($ExcludeUrls)) {
                        $ExcludeUrls = &ParseRelatedLinkExclude($ExcludeUrls);
                    }
                    if (is_array(current($ExcludeUrls))) {
                        $tmpExcludeUrls = array();
                        foreach ($ExcludeUrls as &$_Rule) {
                            if (isset($_Rule['mark']) && !empty($_Rule['mark'])) {
                                if ($_Rule['mark'] != '-' && $_Rule['mark'] != '!')//生成不同于筛选
                                    continue;
                            }
                            if (isset($_Rule['url']) && strlen($_Rule['url']) > 5)
                                $tmpExcludeUrls[] = $_Rule['url'];
                            else{
                                if(isset($_Rule['table']) && isset($_Rule['id']))
                                    {
                                        if(!isset($tmpExcludeUrls[$_Rule['table']])) $tmpExcludeUrls[$_Rule['table']] = array();
                                        $tmpExcludeUrls[$_Rule['table']][] = $_Rule['id'];
                                    }
                                elseif(isset($_Rule['dist']) && isset($_Rule['distid'])){
                                    if(!isset($tmpExcludeUrls[$_Rule['dist']])) $tmpExcludeUrls[$_Rule['dist']] = array();
                                    $tmpExcludeUrls[$_Rule['dist']][] = $_Rule['distid'];
                                }
                            }
                        }
                        unset($ExcludeUrls);
                        $ExcludeUrls = &$tmpExcludeUrls;
                    } else {
                        $ExcludeUrls = null;
                    }
                }
                $counter = 0;
                $ret = array();
                $distdir = array();
                $tmpKey = 'RK' . msectime().strval(mt_rand(1,9999));//防止多线程覆盖
                if ($ExcludeUrls != null) {//预留填充排除的位置
                    $counter = count($ExcludeUrls) * -1;
                }
                foreach ($LabelogTops as $ltk => &$data) {
                    $dist_code = $data['dist'];
                    if ($ExcludeUrls != null) {
                        if (isset($ExcludeUrls[$dist_code])) {
                            if (array_search($data['distid'], $ExcludeUrls[$dist_code]) !== false) {
                                continue;
                            }
                        }
                    }
                    $table = $DistCodes[$dist_code];
                    if (!empty($table)) {
                        if (!isset($distdir[$table])) $distdir[$table] = array();
                        $val = $data['distid'];
                        $distdir[$table][] = &$val;
                        $data[$tmpKey] = &$val;//绑定同一个目标
                        unset($val);
                        if (++$counter >= $LinkCount) break;
                    }
                }

                $UrlRouteCfg = null;
                if (isset($LabelCfg['RelatedLinkTemplate']['urlroute']))
                    $UrlRouteCfg = &$LabelCfg['RelatedLinkTemplate']['urlroute'];

                foreach ($distdir as $table => &$ids) {
                    //获取主键字段名:
                    $priK = $ghx_cache->get_share_var(VAR_TABLE_PRI_KEY, $table);
                    //获取标题字段名:
                    $tipF = $ghx_cache->get_share_var(VAR_TABLE_FIELD_TITLE, $table);
                    $InfoF = null;
                    $InfoF_is_sql = null;
                    $selcols = array($priK, $tipF);
                    $get_info = stripos($template, '{info}');
                    if ($get_info !== false) {
                        $InfoF = $ghx_cache->get_share_var(VAR_TABLE_FIELD_INFO, $table);
                        if (!empty($InfoF)) {
                            $InfoF_is_sql = (strpos($InfoF, ' ') > 0 or strpos($InfoF, '\t') > 0);//就否为sql语句
                            if (!$InfoF_is_sql)
                                $selcols[] = $InfoF;
                        }
                    }
                    $firstimgVar = 'firstimg';
                    $get_firstimg = stripos($template, '{'.$firstimgVar.'}');
                    if($get_firstimg === FALSE){
                        $get_firstimg = stripos($template, '{titleimg}');
                        if ($get_firstimg === false) $firstimgVar = NULL;
                    }
                    $get_firstimg_tb = false;
                    if ($get_firstimg !== false) {
                        if ($table == 'thread') {//主题
                            $get_firstimg_tb = $ghx_cache->get_share_var(VAR_THREADEXT_TB);
                        } elseif ($table == 'post') {//回帖
                            if(!$get_info)  $selcols[] ='message_fmt';//避免重复
                            $get_firstimg_tb = 'post';
                            $firstimgVar = 'message_fmt';
                        }
                    }
                    $selcols = array_unique($selcols);//防止重复
                    $dr = db_find($table, array($priK => $ids), array(), 1, count($ids), $priK, $selcols);
                    if (!empty($dr)) {
                        if ($get_firstimg !== false){
                            if ($table == 'thread'){
                                $drs0 = db_find($get_firstimg_tb,array($priK => $ids),array(), 1, count($ids),$priK,array($priK,$firstimgVar));
                                if(!empty($drs0)){//填充数据:
                                    foreach ($drs0 as $_tid => &$dr0){
                                        $dr[$_tid]['firstimg'] = &$dr0[$firstimgVar];
                                    }
                                    unset($dr0);
                                }
                                unset($drs0);
                            }
                            elseif($get_firstimg_tb == 'post'){
                                foreach ($dr as $pid => &$dr1) {
                                    $message_fmt = &$dr1[$firstimgVar];
                                    //取第一张图片
                                    $matches = null;
                                    if (preg_match('#\<img\s+[^\>]*?\bsrc\s*\=\s*([\'"])?\s*([^\'"\<\>\s]+)\s*([\'"])?#isu', $message_fmt, $matches) > 0) {
                                        $dr1['firstimg'] = trim($matches[2]);
                                    }
                                    else{
                                        //可能是HTML编码了,转换再试:
                                        $exMatches_t = null;
                                        if(preg_match_all('/(\&[\w]{2,10};|\&#\d+)/s',$message_fmt,$exMatches_t,PREG_SET_ORDER)>1){
                                            $message_fmt = html_entity_decode($message_fmt);
                                            if (preg_match('#\<img\s+[^\>]*?\bsrc\s*\=\s*([\'"])([^\'"\<\>]+)\\1#isu', $message_fmt, $matches) > 0) {
                                                $dr1['firstimg'] = trim($matches[2]);
                                            }
                                        }
                                    }
                                    unset($message_fmt);
                                }
                                unset($dr1);
                            }
                        }

                        if ($InfoF_is_sql) {
                            global $tablepre, $uid, $gid,$user;
                            if (strpos($InfoF, '{') !== false && strpos($InfoF, '}') !== false) {
                                $InfoF = str_ireplace('{tablepre}', $tablepre, $InfoF);
                                $InfoF = str_ireplace('{IDS}', implode(',', $ids), $InfoF);
                                $InfoF = str_ireplace('{uid}', $uid, $InfoF);
                                $InfoF = str_ireplace('{gid}', $gid, $InfoF);
                                $InfoF = str_ireplace('{username}',$user['username'] , $InfoF);
                                $InfoF = str_replace('@#_', $tablepre, $InfoF);
                            }
                            $dr1 = db_sql_find($InfoF);
                            if (!empty($dr1)) {
                                foreach ($dr1 as &$infodr) {
                                    if (count($infodr) < 2) break;
                                    $kid = current($infodr);
                                    $dr[$kid]['info'] = next($infodr);
                                }
                                $InfoF = 'info';
                            }
                            unset($dr1);
                        }
                        //回填数据
                        $delids = array();
                        foreach ($ids as $id_k => &$ditem) {
                            $drp = &$dr[$ditem];
                            if ($drp != null) {
                                $my_title = &$drp[$tipF];
                                $title_html_pos1 = strpos($my_title,'<');
                                if($title_html_pos1 > -1){
                                    $title_html_pos2 = strpos($my_title,'>',$title_html_pos1+1);
                                    if($title_html_pos2 > $title_html_pos1){
                                        unset($my_title);
                                        $my_title = strip_tags($drp[$tipF]);
                                    }
                                }
                                $ditem = array('table' => $table, 'id' => $ditem, 'title' => $my_title);
                                unset($my_title,$title_html_pos1);
                                if ($get_info !== false && isset($drp[$InfoF])) {
                                    $ditem['info'] = $drp[$InfoF];
                                }
                                if ($get_firstimg !== false && isset($drp['firstimg'])) {
                                    $ditem['firstimg'] = $drp['firstimg'];
                                }
                                if (!empty($UrlRouteCfg) && !empty($UrlRouteCfg[$table])) {
                                    $urlpre = $UrlRouteCfg[$table];
                                    if (strpos($urlpre, '{') !== false) {
                                        $urlpre = str_ireplace('{ID}', $ditem['id'], $urlpre);
                                        $urlpre = str_ireplace('{TABLE}', $table, $urlpre);
                                    }
                                    $ditem['url'] = url($urlpre);
                                } else {
                                    $ditem['url'] = url($table . $ditem['id']);//默认的路由
                                }
                                if ($ExcludeUrls != null) //是否在排除范围内:
                                {
                                    if (in_array($ditem['url'], $ExcludeUrls,true)) {
                                        $delids[] = $id_k;
                                        //unset($ids[$id_k]); 注:不能在此直接删除使用中的数组元素,否则会导致指针复位!
                                    }
                                }
                            }
                            unset($drp);
                        }
                        if(!empty($delids)){
                            foreach($delids as $delid){
                                unset($ids[$delid]);
                            }
                        }
                        unset($delids);
                    }
                }
                //生成输出数据:
                $ret = array();
                foreach ($LabelogTops as &$data) {
                    $dar = &$data[$tmpKey];
                    if (!empty($dar)) {
                        if ($LinkCount > 0) {
                            $template1 = $template;
                            $reps = 0;
                            foreach ($dar as $dk => &$dv) {
                                $template1 = str_ireplace('{' . $dk . '}', $dv, $template1, $cnt);
                                $reps += $cnt;
                            }
                            $ret[] = &$template1;
                            $LinkCount--;
                            unset($template1);
                        }
                        unset($data[$tmpKey]);//复原,以便于再次被使用
                    }
                    unset($dar);
                }
            }
        }
    }
    return $ret;
}

/**
 * 将Relatedlinks字符串转化成前台可用的键值数组
 * @param string|array &$input_rlinks Relatedlinks字符串
 * @param array|null $fields 要提取的字段列表,参考配置:Label/RelatedLinkTemplate/regexgroup
 * @param int $max_count 最大显示数量,-1为全部
 * @param string|null $matchregex 匹配Relatedlink行的正则式,默认为配置表里的
 * @param array|null $regexgroup 匹配Relatedlink行的正则式结果组命名,默认为配置表里的
 * @return array|bool 失败或者无数据返回false,成功返回键值数组,不存在的项设置为空字符串
 */
function &RelatedlinksToViewArray(&$input_rlinks, array $fields = null, $max_count = -1, $matchregex = null, array &$regexgroup = null)
{
    $ret = false;
    if (!empty($input_rlinks) && $max_count != 0) {
        if ($matchregex == null || empty($regexgroup)) {
            $LabelCfg = &GetPluginCfgNode('Label');
            if ($LabelCfg != null) {
                if ($matchregex == null && isset($LabelCfg['RelatedLinkTemplate']['regex']))
                    $matchregex = $LabelCfg['RelatedLinkTemplate']['regex'];
                if (empty($regexgroup) && isset($LabelCfg['RelatedLinkTemplate']['regexgroup']))
                    $regexgroup = $LabelCfg['RelatedLinkTemplate']['regexgroup'];
            }
        }
        $inputType = 0;
        if (is_array($input_rlinks)) {//检测输入项的类型:
            $test1 = current($input_rlinks);
            if (is_string($test1)) $inputType = 2;
            elseif (is_array($test1)) $inputType = 3;
        } elseif (is_string($input_rlinks))
            $inputType = 1;//字符串
        if (is_string($max_count)) {
            $max_count = intval($max_count);
        }
        if ($inputType > 0 && $matchregex != null && !empty($regexgroup)) {
            if ($inputType == 1)
                $RelatedLinkLines = array_unique(preg_split('#[\\r\\n]+#s', $input_rlinks, -1, PREG_SPLIT_NO_EMPTY));
            else {
                if ($inputType == 2)
                    $RelatedLinkLines = &$input_rlinks;
            }
            if (empty($fields)) {
                $fields = array_values($regexgroup);
            }
            if (is_numeric(key($regexgroup)))
                $regexgroup = array_flip($regexgroup);//交换键值
            $ret = array();
            foreach ($RelatedLinkLines as &$line) {
                if (isset($matches))
                    unset($matches);
                $matches = null;
                if ($inputType == 3)
                    $matches = &$line;
                if ($inputType == 3 || preg_match($matchregex, $line, $matches) > 0) {
                    if (isset($regexgroup['mark'])) {
                        //跳过排除项和注释项
                        if (!empty($matches[$regexgroup['mark']])) {
                            $t_mark = trim($matches[$regexgroup['mark']]);
                            $chr = $t_mark[0];
                            if (($chr == '-' || $chr == '!' || $chr == '?' || strpos($t_mark, '//') === 0)) {
                                continue;
                            }
                            $tord = ord($chr);
                            //第一个字符除了字母数字_和<([之外,其余删除之
                            if (($tord > 47 && $tord < 58) || ($tord > 64 && $tord < 91) || ($tord > 96 && $tord < 123) || $tord == 95 || $tord == 40 || $tord == 60 || $tord == 91) {
                                ;
                            } else {
                                $t_mark = substr($t_mark, 1);
                                $matches[$regexgroup['mark']] = &$t_mark;
                            }
                            if(strlen($t_mark) > 10 && $t_mark[0] == '<' && $t_mark[strlen($t_mark)-1] == '>'){
                                //为HTML标签,检测特殊属性:
                                //开始时间
                                $btime = FindHtmlTagProperty($t_mark,'data-startime');
                                $markshow = true;
                                if($btime !== false) {
                                    $btime = strtotime($btime);
                                    if($btime !== false && $btime < time()) $markshow = false;
                                }
                                if($markshow) {
                                    //结束时间
                                    $etime = FindHtmlTagProperty($t_mark,'data-endtime');
                                    if ($etime !== false) {
                                        $etime = strtotime($etime);
                                        if ($etime !== false && $etime > time()) $markshow = false;
                                    }
                                }
                                if(!$markshow)//不显示此标记
                                    $t_mark = '';
                            }
                        }
                    }
                    $item = array();
                    foreach ($fields as &$field) {
                        if (isset($regexgroup[$field])) {
                            $goid = $regexgroup[$field];
                            if ($goid < count($matches))
                                {
                                    if($field != 'title' && $field != 'info')
                                        $item[$field] = $matches[$goid];
                                    else{
                                        $item[$field] = htmlentities($matches[$goid]);
                                    }
                                }
                            else
                                $item[$field] = '';
                        } else
                            $item[$field] = '';
                    }
                    $ret[] = &$item;
                    unset($item);

                    if ($max_count != -1) {
                        if (count($ret) >= $max_count)
                            break;
                    }
                }
            }
        }
    }
    return $ret;
}

/**
 * 更新关键词关联信息
 * @param string|array $old_labels 原关键词
 * @param string|array $new_labels 新关键词
 * @param array $extended_fields 附带要更新的字段值(必须包含dist和distid),可以多个,与关键词条数一致,或者相同的一条共用
 * @param bool $update_old_labels 是否用$extended_fields更新已经存在的关联记录
 * @param int $setRank 是否设置关键词权重,默认为-1检测配置,false:否,true:是,-1:检测(一行关键词,从左到右,权重值从大到小),不存在则为[2,1]
 * @return array|bool 失败为false<成功返回array('new'=>array({新增的记录}),'del'=>{删除条数},'edit'=>{修改条数});
 */
function UpdateRelatedLink(&$old_labels, &$new_labels, array $extended_fields, $update_old_labels = false, $setRank = -1)
{
    if (!isset($extended_fields['dist']) || empty($extended_fields['dist']) || !isset($extended_fields['distid']) || empty($extended_fields['distid']))
        return false;
    $ret = array();
    if (is_string($old_labels) && is_string($new_labels) && $old_labels == $new_labels && !$update_old_labels)
        return $ret;
    if($setRank == -1){
        $AKAWCfg = &GetPluginCfgNode('ArticleKeyWord/ArrangementWeights');
        if($AKAWCfg != null && !empty($AKAWCfg))
            $setRank = true;
        else
            $setRank = false;
    }
    $table = &$extended_fields['dist'];
    $pkID = &$extended_fields['distid'];
    $labelIds_o = &StrToLabel($old_labels, $table, false, $setRank);
    $labelIds_n = &StrToLabel($new_labels, $table, true, $setRank);//不存在则新增标签
    if ($labelIds_o === false || $labelIds_n === false) return false;
    $lid_del_k = array_keys($labelIds_o);
    $lid_insert_k = array_keys($labelIds_n);
    $lid_upd_ranks = array();//需要更新rank的
    if (!$update_old_labels) {
        //删除完全相同的关键词组:
        $wd_len = min(count($lid_del_k), count($lid_insert_k));
        $hasDel = 0;
        for ($d = 0; $d < $wd_len; $d++) {
            if ($lid_del_k[$d] == $lid_insert_k[$d]) {
                unset($labelIds_o[$lid_del_k[$d]], $labelIds_n[$lid_del_k[$d]]);
                $hasDel++;
            }
        }
        if ($hasDel > 0) {//重新获取键名
            $lid_del_k = array_keys($labelIds_o);
            $lid_insert_k = array_keys($labelIds_n);
        }
        unset($hasDel, $wd_len);
    }

    foreach ($lid_insert_k as $k => &$v) {
        $lid_del_kF = array_search($v, $lid_del_k);
        if ($lid_del_kF !== false) {
            if (isset($labelIds_n[$v]['rank'])) {
                if (!isset($labelIds_o[$v]['rank']) || $labelIds_o[$v]['rank'] != $labelIds_n[$v]['rank'])
                    $lid_upd_ranks[$v] = &$labelIds_n[$v];//需要更新
            }
            unset($lid_insert_k[$k], $lid_del_k[$lid_del_kF]);
        }
    }

    $ins = $dels = $upds = null;//声明
    //转换成{分表}=>array({lid}...)的格式:
    if (!empty($lid_insert_k)) {
        $ins = array();
        foreach ($lid_insert_k as &$_v2) {
            $tmpKV = &$labelIds_n[$_v2];
            if ($tmpKV !== null) {
                $ins[] = array('lid' => &$tmpKV);
                unset($tmpKV);
            }
        }
    }
    if (!empty($lid_del_k)) {
        $dels = array();
        foreach ($lid_del_k as &$_v3) {
            $tmpKV = &$labelIds_o[$_v3];
            if ($tmpKV !== null) {
                //array({name}=>array({id},{name},{subtable},{cate}))
                $dels[] = array('lid' => &$tmpKV);
                unset($tmpKV);
            }
        }
    }
    if (!empty($lid_upd_ranks)) {
        $upds = array();
        foreach ($lid_upd_ranks as $_k4 => &$_v4) {
            $tmpKV = &$labelIds_n[$_k4];
            if ($tmpKV !== null) {
                if (isset($tmpKV['subtable']) && isset($tmpKV['rank'])) {
                    $upds[] = &$tmpKV;
                }
                unset($tmpKV);
            }
        }
    }


    unset($extended_fields['lid']);//清除字段值设置里的lid,应该在$old_labels和$new_labels中指定
    if (!isset($extended_fields['atime'])) $extended_fields['atime'] = time();//设置默认时间

    $labellogTB = GhxCache::getInstance()->get_share_var(VAR_LABELOG_TB);
    $fields = GhxCache::getInstance()->get_share_var(VAR_TABLE_COLUMNS, $labellogTB . '0');
    $extended_fields = array_intersect_key($extended_fields, $fields);//过滤无效字段

    if (!empty($ins)) {//有新增:
        $ret['new'] = AddLabelLog($ins, $pkID, $table, $extended_fields);
    }
    if (!empty($dels)) {//有删除:
        $ret['del'] = DelLabelLog($dels, $pkID, $table);
    }
    if (!empty($upds) && !$update_old_labels && !isset($extended_fields['rank'])) {
        $lcno = 0;
        if (!is_numeric($table)) {
            $LabelCfg = &GetPluginCfgNode('Label');//引用更快
            if (!empty($table)) {
                if (isset($LabelCfg['distcode'][$table]))
                    $lcno = intval($LabelCfg['distcode'][$table]['id']);
            }
        } else
            $lcno = intval($table);
        $updCond = array('lid' => 0, 'dist' => $lcno, 'distid' => $pkID);
        $updFields = array('rank' => 0);
        foreach ($upds as &$lidarr) {//subtable
            //只能一条条更新:
            $tableName = $labellogTB . $lidarr['subtable'];
            $updCond['lid'] = $lidarr['id'];
            $updFields['rank'] = $lidarr['rank'];
            $updret = db_update($tableName, $updCond, $updFields);
            if ($updret > 0) {
                if (!isset($ret['upd'])) $ret['upd'] = array();
                $ret['upd'][] = &$lidarr;
            }
        }
    }
    if (empty($ins) && empty($dels) && !empty($lid_del_k) && !empty($extended_fields) && $update_old_labels) {//有更新
        $ret['edit'] = UpdateLabelLog($labelIds_o, $extended_fields, $pkID, $table);
    }
    return $ret;
}

/**
 * 批量重新生成threadext表的RelatedLinks
 * @param array $threads_data 要更新的threadext记录行,或者查询threadext的条件(包含updatetime:更新在此时间之前的;aflags:标志值位运算)
 * @param string $Dist labelog{X}表的dist
 * @return array|false 成功则返回array({tid}=>{New_RelatedLinks}...)
 */
function BulkUpdateRelatedLinks(array &$threads_data, $Dist = 'thread')
{
    $ret = array();
    if (!empty($threads_data)) {
        $theadext_table = GhxCache::getInstance()->get_share_var(VAR_THREADEXT_TB);
        $detectrow1 = current($threads_data);
        if (!is_array($detectrow1) || !isset($detectrow1['relatedfids'])) {
            if (!isset($threads_data['updatetime'])) return false;
            if (!is_numeric($threads_data['updatetime']))
                $OldTimeLimit = strtotime($threads_data['updatetime']);
            else
                $OldTimeLimit = intval($threads_data['updatetime']);
            $searchConds = array('relatedfids' => array('<>' => '-1'), 'relatedupdtime' => array('<=' => $OldTimeLimit));
            if (isset($threads_data['aflags']))
                $searchConds['aflags'] = array('*0<`aflags`|' => intval($threads_data['aflags']));
            $readcount = intval(isset($threads_data['count'])?$threads_data['count']:512);
            $old_labels = db_find($theadext_table, $searchConds, array('tid' => 0), 1, $readcount, 'tid', array('tid', 'keywords', 'relatedfids', 'relatedlinks', 'relatedupdtime', 'aflags'));//expired<0可能是定时发布任务
            if (empty($old_labels)) return $ret;
            $threads_data = &$old_labels;
            $detectrow1 = current($threads_data);
        }
        if (!isset($detectrow1['relatedfids'])) return $ret;
        unset($detectrow1);
        //整理,以relatedfids为键:
        $todolist = $todologs = $threadlabels = array();

        foreach ($threads_data as &$threads_da) {
            $relatedfid = &$threads_da['relatedfids'];
            if ($relatedfid != '-1') {
                //----------其它处理---------------
                //todo: 其它处理
                //-------------------------------
                $kw = &$threads_da['keywords'];
                if (!empty($kw)) {
                    $kws = &StrToLabel($kw);
                    if (!empty($kws)) {
                        foreach ($kws as $k => &$v) {
                            if (!isset($todolist[$k]))
                                $todolist[$k] = &$v;
                        }
                        $threadlabels[] = array(&$relatedfid, &$kws, &$threads_da);
                    }
                    unset($kws);
                }
                unset($kw);
            }
            unset($relatedfid);
        }
        if (!empty($todolist)) {//已经得到所有关键词
            $listcount = count($todolist);
            $todolist_keys = array_keys($todolist);
            $workers = array();
            $pos = 0;
            while ($pos < $listcount) {
                $y = 0;
                while ($pos < $listcount && $y < 128) {
                    $workers[$todolist_keys[$pos]] = &$todolist[$todolist_keys[$pos]];
                    $pos++;
                    $y++;
                }
                $search_lg_conditions = array('disabled' => 0, 'dist' => $Dist);
                $Labelogs = &ListLabelog($workers, $search_lg_conditions);
                if (!empty($Labelogs)) {
                    //foreach比array_merge快
                    foreach ($Labelogs as &$labelog)
                        $todologs[] = &$labelog;
                }
                unset($Labelogs);
            }
        }
        if (!empty($todologs)) {
            $RelatedLinkViewCount = &GetPluginCfgNode('ArticleKeyWord/RelatedLinkViewCount');
            //$threadlabel:array(&$relatedfid,&$kws,&$threads_da);
            foreach ($threadlabels as &$threadlabel) {
                //&SelectiveLabelogs(array &$Labelogs, array &$LabelIds,array &$ExcludeDistRows=null, $pick_count = -1, $SortItem = 'rank', int $ItemType = 0, $increase = 0, float $NewestWeights = -1)
                $_threadext_d = &$threadlabel[2];
                $ExcludeDistRows = array(array('table' => $Dist, 'id' => $_threadext_d['tid']));
                $relatedlinks = &$_threadext_d['relatedlinks'];
                $relatedlinkrules =  &ParseRelatedLinkExclude($relatedlinks);
                $related_exclude1 = array();
                $must_includes = 0;
                unset($relatedlinks);
                if (!empty($relatedlinkrules)) {
                    foreach ($relatedlinkrules as &$relatedlinkrule) {
                        if (isset($relatedlinkrule['mark']) && !empty($relatedlinkrule['mark'])) {
                            {
                                $related_exclude1[] = &$relatedlinkrule['RawLine'];
                                $ExcludeDistRows[] = &$relatedlinkrule;
                                if ($relatedlinkrule['mark'] == '+' || $relatedlinkrule['mark'] == '*')
                                    $must_includes++;
                            }
                        }
                    }
                    $relatedlinks = implode(PHP_EOL, $related_exclude1);
                } else {
                    $relatedlinks = NULL;
                }
                $selLabelogs = &SelectiveLabelogs($todologs, $threadlabel[1], $threadlabel[0], $ExcludeDistRows, $RelatedLinkViewCount - $must_includes);
                if (!empty($selLabelogs)) {
                    //此步与上步的排除不同:
                    $exCount = count($ExcludeDistRows);
                    $ExcludeDistRows2 = array();
                    for ($d = 0; $d < $exCount; $d++) {
                        $_E = &$ExcludeDistRows[$d];
                        if (isset($_E['mark'])) {
                            if ($_E['mark'] == '!' || $_E['mark'] == '-') {
                                $ExcludeDistRows2[] = &$_E;
                            }
                        } else {
                            if (isset($_E['table']) || isset($_E['dist'])) {
                                $ExcludeDistRows2[] = &$_E;
                            }
                        }
                        unset($_E);
                    }
                    $related_url_fmt = &GenerateRelatedLinks($selLabelogs, 10 - $must_includes, $ExcludeDistRows2);
                    if ($relatedlinks === null) $relatedlinks = '';
                    if (!empty($related_url_fmt)) {
                        $relatedlinks .= PHP_EOL .implode(PHP_EOL, $related_url_fmt);
                    }
                    if (db_update($theadext_table, array('tid' => $_threadext_d['tid']), array('relatedlinks' => &$relatedlinks,'relatedupdtime'=>time())) > 0) {
                        $ret[$_threadext_d['tid']] = &$relatedlinks;
                    }
                    unset($related_url_fmt, $ExcludeDistRows2);
                }
                unset($selLabelogs, $_threadext_d, $relatedlinks, $related_exclude1, $relatedlinkrules);
            }
        }
        unset($todologs, $workers,$todolist);
    }
    return $ret;
}

/**
 * 获取前台模板目录数组
 * @param string $type 类型:thread/post
 * @param bool $refresh 是否更新列表,默认为否
 * @return array
 */
function &GetViewTemplateList(string $type='thread',bool $refresh=false){
    $ret = &GhxCache::getInstance()->get_share_var(VAR_VIEW_TEMPLATE_LIST,$type,$refresh);
    return $ret;
}

/**
 * 获取指定版块的默认模板配置
 * @param string|int $forum 版块
 * @param string $route 路由,Thread,Post之类
 * @param array $alt_tpl 为空时的替代模板
 * @param array|null $forum_list $forum为版块名称时需要从其中索引出数据
 * @return array|null 结果中可能包含PHP文件路径
 */
function &GetForumDefaultTemplate($forum, string $route='Thread',array $alt_tpl=array("view/htm/thread.htm"),array &$forum_list=null){
    $nullval = null;
    if(is_string($forum) && !ctype_digit($forum)){
        if(empty($forum_list)){
            global $forumlist;
            $forum_list = &$forumlist;
        }
        if(!empty($forum_list)){
            foreach ($forum_list as $fid => &$fdata){
                if($fdata['name'] == $forum){
                    $forum = intval($fid);
                    break;
                }
            }
            unset($fdata);
        }
        if(is_int($forum)) return $nullval;
    }
    else
        $forum = intval($forum);
    $ViewTemplate = &GetPluginCfgNode('ViewTemplate');
    if(!empty($ViewTemplate)){
        $ForumDefault = null;
        if(!isset($ViewTemplate[$route])){
            $route = ucfirst($route);
        }
        if(isset($ViewTemplate[$route]))
            $ForumDefault = &$ViewTemplate[$route]['ForumDefault'];
        $ViewTemplateName = null;
        if(!empty($ForumDefault)) {
            if (isset($ForumDefault[$forum])) $ViewTemplateName = $ForumDefault[$forum];
            else $ViewTemplateName = (isset($ForumDefault[0]) ? $ForumDefault[0] : null);
            if (!empty($ViewTemplateName) && isset($ViewTemplate[$forum]['ViewTemplateList'])) {
                if (isset($ViewTemplate[$forum]['ViewTemplateList'][$ViewTemplateName]))
                    return $ViewTemplate[$forum]['ViewTemplateList'][$ViewTemplateName];
            }
        }
    }
    return $alt_tpl;
}


// 编辑回帖,支持message为空的情况
function post_update_support_empty($pid, $arr, $tid = 0) {
    if(isset($arr['message']) && ($arr['message'] !== null))
        return post_update($pid, $arr, $tid);//message不为空时调用原函数

    global $conf, $user, $gid;

    $post = post__read($pid);
    if(empty($post)) return FALSE;
    $tid = $post['tid'];
    $uid = $post['uid'];
    $isfirst = $post['isfirst'];

    // hook model_post_update_start.php



    // hook model_post_create_post__create_before.php

    $r = post__update($pid, $arr);

    attach_assoc_post($pid);

    // hook model_post_update_end.php
    return $r;
}



/**
 * 去掉代码中的注释
 * 调用示例:Strip_Whitespace(APP_PATH.'ghx','/.+?\.php$/i');
 * @param string $path 目录
 * @return int 成功修改的文件数量
 */
function Strip_Whitespace(string $path,string $matchreg=null)
{
    $ret = 0;
    if (!is_dir($path)) return $ret;
    $allfiles = &get_filenames_ci3($path, true, true);
    foreach ($allfiles as &$src) {
        if(!empty($matchreg)){
            if(preg_match($matchreg,$src) == 0) continue;
        }
        $contents = file_get_contents($src);
        $repcounts = $repcounts0 = 0;
        $ischg = false;
        $contents2 = preg_replace('#(\n\s*)//(\s*hook\s+[\w\-\.]+)(\s*?\n)#us', '$1H_O_O_K(\'$2\');$3', $contents, -1, $repcounts);
        $num = strpos($contents2, '<<<');  // heredoc 是否存在。
        $str = "";
        $arr = array();
        $repcounts0 = $repcounts;
        if ($num !== false) {  // heredoc 存在。只去除注释不压缩。
            $lr = new LabelReplacer();
            $lr->vars = &$arr;
            $matchret = null;
            if (($matchret = preg_replace_callback('/([^\<\^])(\<\<\<(\w+).+?\n\3);/su', array($lr, 'ReplaceHeredoc'), $contents2, -1, $repcounts)) !== null) {
                if ($repcounts > 0) {
                    $contents2 = &$matchret;
                    $ischg = true;
                    unset($matchret);
                }
            }
        }

        if ($repcounts > 0) {
            file_put_contents_try($src, $contents2);
        }
        $str = php_strip_whitespace($src); // 不存在 heredoc 。因为他会错误解析
        if ($str === null) {//恢复
            $ischg = false;
        } elseif ($str != $contents || $repcounts > 0) {
            $ischg = true;
        }

        if ($ischg) {
            if ($repcounts > 0) {
                $repcounts2 = 0;
                $str = preg_replace('/\bH_O_O_K\\(\'([^\']+)\'\\);/us', "\n//$1\n", $str, -1, $repcounts2);
                if ($repcounts2 != $repcounts0) $ischg = false;
            }
            if (!empty($arr) && $ischg) {
                foreach ($arr as $k => &$v) {
                    $str = str_replace("'{$k}'", $v, $str);
                }
            }
        }
        if ($ischg)
            file_put_contents_try($src, $str);
        else {
            file_put_contents_try($src, $contents);
        }
        unset($contents, $contents2, $str,$arr);
        if ($ischg) $ret++;
    }
    return $ret;
}